org.springframework.ide.eclipse.beans.ui.properties.ConfigSetDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ide.eclipse.beans.ui.properties.ConfigSetDialog.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2014 Spring IDE Developers
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Spring IDE Developers - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.eclipse.beans.ui.properties;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalListener;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansConfig;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansConfigFactory;
import org.springframework.ide.eclipse.beans.core.model.IBeansComponent;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig.Type;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfigSet;
import org.springframework.ide.eclipse.beans.core.model.IBeansModel;
import org.springframework.ide.eclipse.beans.core.model.IBeansProject;
import org.springframework.ide.eclipse.beans.core.model.IProfileAwareBeansComponent;
import org.springframework.ide.eclipse.beans.ui.BeansUIImages;
import org.springframework.ide.eclipse.beans.ui.BeansUIPlugin;
import org.springframework.ide.eclipse.beans.ui.properties.model.PropertiesConfigSet;
import org.springframework.ide.eclipse.beans.ui.properties.model.PropertiesModelLabelProvider;
import org.springframework.ide.eclipse.beans.ui.properties.model.PropertiesProject;
import org.springframework.ide.eclipse.core.model.ModelUtils;
import org.springsource.ide.eclipse.commons.ui.SpringUIUtils;
import org.springframework.util.StringUtils;

/**
 * Dialog for creating a beans config set.
 * @author Torsten Juergeleit
 * @author Christian Dupuis
 * @author Martin Lippert
 * @author Terry Denney
 */
public class ConfigSetDialog extends Dialog {

    private static final String PREFIX = "ConfigSetDialog.";

    private static final String TITLE_NEW = PREFIX + "title.new";
    private static final String TITLE_EDIT = PREFIX + "title.edit";

    private static final String ERROR_INVALID_NAME = PREFIX + "error.invalidName";
    private static final String ERROR_USED_NAME = PREFIX + "error.usedName";

    private static final String NAME_LABEL = PREFIX + "name.label";
    private static final String PROFILE_LABEL = PREFIX + "profiles.label";
    private static final String OVERRIDE_LABEL = PREFIX + "override.label";
    private static final String INCOMPLETE_LABEL = PREFIX + "incomplete.label";
    private static final String VIEWER_LABEL = PREFIX + "viewer.label";
    private static final String SELECT_ALL_LABEL = PREFIX + "select.all.label";
    private static final String DESELECT_ALL_LABEL = PREFIX + "deselect.all.label";

    private static final int LIST_VIEWER_WIDTH = 400;
    private static final int LIST_VIEWER_HEIGHT = 250;

    private CLabel messageLabel;

    private Text nameText;
    private Text profilesText;

    private Button overrideButton;
    private Button incompleteButton;
    private CheckboxTableViewer configsViewer;
    private Label errorLabel;
    private Button okButton;

    private PropertiesProject project;
    private PropertiesConfigSet configSet;
    private String title;

    private enum Mode {
        NEW, EDIT
    };

    private Mode mode;

    public ConfigSetDialog(Shell parent, PropertiesProject project, String configSetName,
            IBeansConfigSet.Type type) {
        super(parent);
        this.project = project;
        if (configSetName == null) {
            configSet = new PropertiesConfigSet(project, (String) null, type);
            title = BeansUIPlugin.getResourceString(TITLE_NEW);
            mode = Mode.NEW;
        } else {
            configSet = (PropertiesConfigSet) project.getConfigSet(configSetName);
            title = BeansUIPlugin.getResourceString(TITLE_EDIT);
            mode = Mode.EDIT;
        }
    }

    @Override
    protected void configureShell(Shell shell) {
        super.configureShell(shell);
        if (title != null) {
            shell.setText(title);
        }
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite composite = (Composite) super.createDialogArea(parent);

        // Create group composite for options
        Composite optionsGroup = new Composite(composite, SWT.NULL);
        optionsGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        GridLayout layout = new GridLayout();
        optionsGroup.setLayout(layout);

        messageLabel = new CLabel(optionsGroup, SWT.NONE);
        messageLabel.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));

        // Create labeled name text field
        nameText = SpringUIUtils.createTextField(optionsGroup, BeansUIPlugin.getResourceString(NAME_LABEL), 0, 0,
                50);
        nameText.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validateName();
            }
        });

        // Create labeled profiles text field
        profilesText = SpringUIUtils.createTextField(optionsGroup, BeansUIPlugin.getResourceString(PROFILE_LABEL),
                0, 0, 50);
        profilesText.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validateProfiles();
            }
        });
        profilesText.setText(StringUtils.collectionToDelimitedString(configSet.getProfiles(), ", "));
        createProfilesContentAssist();

        Label options = new Label(optionsGroup, SWT.WRAP);
        options.setText("Options:");
        options.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        // Create labeled checkboxes
        overrideButton = SpringUIUtils.createCheckBox(optionsGroup,
                BeansUIPlugin.getResourceString(OVERRIDE_LABEL));
        overrideButton.setSelection(configSet.isAllowBeanDefinitionOverriding());
        incompleteButton = SpringUIUtils.createCheckBox(optionsGroup,
                BeansUIPlugin.getResourceString(INCOMPLETE_LABEL));
        incompleteButton.setSelection(configSet.isIncomplete());

        // Create config set list viewer
        Label viewerLabel = new Label(composite, SWT.NONE);
        GridData gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
        viewerLabel.setLayoutData(gd);
        viewerLabel.setText(BeansUIPlugin.getResourceString(VIEWER_LABEL));

        configsViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER);
        gd = new GridData(GridData.FILL_BOTH);
        gd.widthHint = LIST_VIEWER_WIDTH;
        gd.heightHint = LIST_VIEWER_HEIGHT;

        configsViewer.getTable().setLayoutData(gd);
        configsViewer.setContentProvider(new ConfigFilesContentProvider(createConfigList()));
        configsViewer.setLabelProvider(new PropertiesModelLabelProvider());
        configsViewer.setSorter(new ConfigFilesSorter());
        configsViewer.setInput(this); // activate content provider
        configsViewer.setCheckedElements(configSet.getConfigs().toArray());
        configsViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            public void selectionChanged(SelectionChangedEvent event) {
                validateProfiles();
            }
        });

        // Create select all and deselect all buttons
        Composite buttonsGroup = new Composite(composite, SWT.NULL);
        buttonsGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        layout = new GridLayout();
        layout.numColumns = 2;
        layout.marginTop = 0;
        layout.marginBottom = 10;
        buttonsGroup.setLayout(layout);
        buttonsGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        SpringUIUtils.createButton(buttonsGroup, BeansUIPlugin.getResourceString(SELECT_ALL_LABEL),
                new SelectionAdapter() {

                    public void widgetSelected(SelectionEvent e) {
                        configsViewer.setAllChecked(true);
                    }
                });

        SpringUIUtils.createButton(buttonsGroup, BeansUIPlugin.getResourceString(DESELECT_ALL_LABEL),
                new SelectionAdapter() {

                    public void widgetSelected(SelectionEvent e) {
                        configsViewer.setAllChecked(false);
                    }
                });

        // Create error label
        errorLabel = new Label(composite, SWT.NONE);
        errorLabel.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
        errorLabel.setForeground(JFaceColors.getErrorText(parent.getDisplay()));
        errorLabel.setBackground(JFaceColors.getErrorBackground(parent.getDisplay()));
        applyDialogFont(composite);
        return composite;
    }

    private Set<String> getDefinedProfiles(IBeansComponent component, String filter, Set<String> existingProfiles) {
        Set<String> profiles = new HashSet<String>();
        if (component instanceof IProfileAwareBeansComponent) {
            Set<String> currentProfiles = ((IProfileAwareBeansComponent) component).getProfiles();
            for (String profile : currentProfiles) {
                if (profile.startsWith(filter) && !existingProfiles.contains(profile)) {
                    profiles.add(profile);
                }
            }
        }

        Set<IBeansComponent> children = component.getComponents();
        for (IBeansComponent child : children) {
            profiles.addAll(getDefinedProfiles(child, filter, existingProfiles));
        }

        return profiles;
    }

    private class ProfileContentProposal implements IContentProposal {

        private final String profile;

        private final int offset;

        private final int cursorPosition;

        public ProfileContentProposal(String profile, int offset, int cursorPosition) {
            this.profile = profile;
            this.offset = offset;
            this.cursorPosition = cursorPosition;
        }

        public String getLabel() {
            return profile;
        }

        public String getDescription() {
            Set<IBeansConfig> configs = getDefiningConfigForProfile(profile);
            Set<String> configNames = new HashSet<String>();
            for (IBeansConfig config : configs) {
                configNames.add(config.getElementName());
            }

            return "Profile \"" + profile + "\" defined in "
                    + StringUtils.collectionToDelimitedString(configNames, ", ");
        }

        public int getCursorPosition() {
            return offset + profile.length();
        }

        public String getContent() {
            return profile.substring(cursorPosition - offset, profile.length());
        }

        public String getProfile() {
            return profile;
        }
    }

    private void createProfilesContentAssist() {
        IContentProposalProvider proposalProvider = new IContentProposalProvider() {

            public IContentProposal[] getProposals(String contents, final int position) {
                String prefix = contents.substring(0, position);
                String filter = prefix;
                Set<String> existingProfiles = new HashSet<String>();

                int separatorIndex = -1;
                while (filter.contains(",")) {
                    separatorIndex = prefix.indexOf(",");
                    if (separatorIndex >= 0) {
                        existingProfiles.add(prefix.substring(0, separatorIndex));
                        filter = prefix.substring(separatorIndex + 1);
                        while (filter.startsWith(" ")) {
                            separatorIndex++;
                            filter = filter.substring(1);
                        }
                    }
                }

                Set<IBeansConfig> configs = project.getConfigs();
                Set<String> profiles = new HashSet<String>();
                for (IBeansConfig config : configs) {
                    Set<IBeansComponent> components = config.getComponents();
                    for (IBeansComponent component : components) {
                        profiles.addAll(getDefinedProfiles(component, filter, existingProfiles));
                    }
                }

                List<IContentProposal> proposals = new ArrayList<IContentProposal>();
                for (final String profile : profiles) {
                    proposals.add(new ProfileContentProposal(profile, separatorIndex + 1, position));
                }

                Collections.sort(proposals, new Comparator<IContentProposal>() {
                    public int compare(IContentProposal o1, IContentProposal o2) {
                        return o1.getLabel().compareTo(o2.getLabel());
                    }
                });
                return proposals.toArray(new IContentProposal[proposals.size()]);
            }
        };
        try {
            KeyStroke keyStroke = KeyStroke.getInstance("Ctrl+Space");
            ContentProposalAdapter contentProposalAdapter = new ContentProposalAdapter(profilesText,
                    new TextContentAdapter(), proposalProvider, keyStroke, null);
            contentProposalAdapter.addContentProposalListener(new IContentProposalListener() {

                public void proposalAccepted(IContentProposal proposal) {
                    if (proposal instanceof ProfileContentProposal) {
                        ProfileContentProposal profileProposal = (ProfileContentProposal) proposal;
                        Set<IBeansConfig> configs = getDefiningConfigForProfile(profileProposal.getProfile());
                        for (IBeansConfig config : configs) {
                            if (!configsViewer.getChecked(config)) {
                                configsViewer.setChecked(config, true);
                            }
                        }

                        validateProfiles();
                    }
                }
            });
        } catch (ParseException e) {
        }
    }

    @Override
    protected void createButtonsForButtonBar(Composite parent) {

        // Create OK and Cancel buttons by default
        okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
        // Do this here because setting the text will set enablement on the
        // ok button
        nameText.setFocus();
        String name = configSet.getElementName();
        if (name != null && name.trim().length() != 0) {
            nameText.setText(name);
            okButton.setEnabled(true);
        } else {
            okButton.setEnabled(false);
        }
    }

    @Override
    protected void buttonPressed(int buttonId) {
        if (buttonId == IDialogConstants.OK_ID) {

            // Remove old config set from project
            if (mode == Mode.EDIT) {
                project.removeConfigSet(configSet.getElementName());
            }

            // Update config set
            configSet.setElementName(nameText.getText());
            configSet.setAllowBeanDefinitionOverriding(overrideButton.getSelection());
            configSet.setIncomplete(incompleteButton.getSelection());

            Set<String> profiles = new LinkedHashSet<String>();
            if (profilesText.getText().length() > 0) {
                String[] profilesSpec = StringUtils.tokenizeToStringArray(profilesText.getText(),
                        BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                for (String profile : profilesSpec) {
                    profiles.add(profile);
                }
            }
            configSet.setProfiles(profiles);

            // Before removing all configs from this config set keep a copy of
            // the original list of configs in the config set
            Set<IBeansConfig> oldConfigs = new LinkedHashSet<IBeansConfig>(configSet.getConfigs());
            configSet.removeAllConfigs();

            // At first add the originally and still selected configs to the
            // config set to preserve their order
            List newConfigs = Arrays.asList(configsViewer.getCheckedElements());
            for (IBeansConfig config : oldConfigs) {
                if (newConfigs.contains(config)) {
                    configSet.addConfig(config.getElementName());
                }
            }

            // Finally add the newly selected configs to the config set
            for (Object newConfig : newConfigs) {
                IBeansConfig config = (IBeansConfig) newConfig;
                String configName = config.getElementName();
                if (!configSet.hasConfig(configName)) {
                    configSet.addConfig(configName);
                }
            }

            // Read updated or newly created config set
            project.addConfigSet(configSet);
        }
        super.buttonPressed(buttonId);
    }

    private List<IBeansConfig> createConfigList() {

        // Create new list with all config files from this config set
        List<IBeansConfig> configs = new ArrayList<IBeansConfig>(configSet.getConfigs());

        // Add additional configs from the selected and referenced projects
        addConfigsFromReferencedProjects(project, configs, new HashSet<IProject>(), false);

        return configs;
    }

    private void addConfigsFromReferencedProjects(IBeansProject beansProject, List<IBeansConfig> configs,
            Set<IProject> projects, boolean addProjectPath) {
        if (beansProject == null || projects.contains(beansProject.getProject())) {
            return;
        }

        projects.add(beansProject.getProject());
        IBeansModel model = BeansCorePlugin.getModel();
        try {
            for (IBeansConfig config : beansProject.getConfigs()) {
                String projectPath = ModelUtils.getResourcePath(config.getElementParent());
                if (projectPath != null) {

                    // Create the full qualified path of the config
                    // (with support for configs stored in JAR files)
                    // as long as its not the initiating project
                    String name = addProjectPath && (config instanceof BeansConfig)
                            ? projectPath + "/" + config.getElementName()
                            : config.getElementName();
                    if (!configSet.hasConfig(name)) {
                        configs.add(BeansConfigFactory.create(beansProject, name, Type.MANUAL, false));
                    }
                }
            }

            // Recursively add configurations to project
            for (IProject proj : beansProject.getProject().getProject().getReferencedProjects()) {
                IBeansProject referencedProj = model.getProject(proj);
                addConfigsFromReferencedProjects(referencedProj, configs, projects, true);
            }
        } catch (CoreException e) {
            // We can't do anything here
        }
    }

    private Set<IBeansConfig> getDefiningConfigForProfile(String profile) {
        Set<IBeansConfig> configs = project.getConfigs();
        Set<IBeansConfig> definingConfigs = new HashSet<IBeansConfig>();
        for (IBeansConfig config : configs) {
            Set<IBeansComponent> components = config.getComponents();
            for (IBeansComponent component : components) {
                Set<String> existingProfiles = Collections.emptySet();
                if (getDefinedProfiles(component, "", existingProfiles).contains(profile)) {
                    definingConfigs.add(config);
                    break;
                }
            }
        }

        return definingConfigs;
    }

    private void validateProfiles() {
        if (configsViewer != null) {
            String[] profiles = StringUtils.tokenizeToStringArray(profilesText.getText(),
                    BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            List configs = Arrays.asList(configsViewer.getCheckedElements());
            Set<String> unselectedProfiles = new HashSet<String>();

            for (String profile : profiles) {
                boolean configSelected = false;
                for (Object obj : configs) {
                    IBeansConfig config = (IBeansConfig) obj;
                    Set<IBeansComponent> components = config.getComponents();
                    for (IBeansComponent component : components) {
                        Set<String> existingProfiles = Collections.emptySet();
                        if (getDefinedProfiles(component, "", existingProfiles).contains(profile)) {
                            configSelected = true;
                        }
                    }
                }

                if (!configSelected) {
                    unselectedProfiles.add(profile);
                }
            }

            if (unselectedProfiles.size() > 0) {
                messageLabel.setImage(BeansUIImages.getImage(BeansUIImages.IMG_OBJS_WARNING));
                messageLabel.setText("Profiles selected ("
                        + StringUtils.arrayToDelimitedString(unselectedProfiles.toArray(), ", ")
                        + ") are not defined in selected config files.");
                return;
            }

            for (String profile : profiles) {
                Set<IBeansConfig> definingConfigs = getDefiningConfigForProfile(profile);
                for (IBeansConfig definingConfig : definingConfigs) {
                    if (!configs.contains(definingConfig)) {
                        messageLabel.setImage(BeansUIImages.getImage(BeansUIImages.IMG_OBJS_WARNING));
                        messageLabel.setText("Selected config files do not completely define profiles selected.");
                        return;
                    }
                }
            }

        }

        messageLabel.setImage(null);
        if (mode == Mode.NEW) {
            messageLabel.setText("Create a new config set.");
        } else {
            messageLabel.setText("Edit config set.");
        }
    }

    private void validateName() {
        boolean isEnabled = false;

        String name = nameText.getText();
        if (name == null || name.trim().length() == 0) {
            errorLabel.setText(BeansUIPlugin.getResourceString(ERROR_INVALID_NAME));
        } else if (mode == Mode.NEW || !name.equals(configSet.getElementName())) {
            if (project.hasConfigSet(name)) {
                errorLabel.setText(BeansUIPlugin.getResourceString(ERROR_USED_NAME));
            } else {
                errorLabel.setText("");
                isEnabled = true;
            }
        } else {
            errorLabel.setText("");
            isEnabled = true;
        }

        okButton.setEnabled(isEnabled);
        errorLabel.getParent().update();
    }

    private static class ConfigFilesContentProvider implements IStructuredContentProvider {

        private List<IBeansConfig> configs;

        public ConfigFilesContentProvider(List<IBeansConfig> configs) {
            this.configs = configs;
        }

        public Object[] getElements(Object obj) {
            return configs.toArray();
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }

        public void dispose() {
        }
    }

    private static class ConfigFilesSorter extends ViewerSorter {

        private enum Category {
            SUB_DIR, ROOT_DIR, OTHER
        };

        @Override
        public int category(Object element) {
            if (element instanceof IBeansConfig) {
                if (((IBeansConfig) element).getElementName().indexOf('/') == -1) {
                    return Category.ROOT_DIR.ordinal();
                }
                return Category.SUB_DIR.ordinal();
            }
            return Category.OTHER.ordinal();
        }
    }
}