eu.numberfour.n4js.n4mf.ui.wizard.N4MFWizardNewProjectCreationPage.java Source code

Java tutorial

Introduction

Here is the source code for eu.numberfour.n4js.n4mf.ui.wizard.N4MFWizardNewProjectCreationPage.java

Source

/**
 * Copyright (c) 2016 NumberFour AG.
 * 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:
 *   NumberFour AG - Initial API and implementation
 */
package eu.numberfour.n4js.n4mf.ui.wizard;

import static com.google.common.base.CharMatcher.BREAKING_WHITESPACE;
import static com.google.common.base.CharMatcher.JAVA_LETTER;
import static com.google.common.base.CharMatcher.is;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
import static eu.numberfour.n4js.n4mf.ProjectType.API;
import static eu.numberfour.n4js.n4mf.ProjectType.LIBRARY;
import static eu.numberfour.n4js.n4mf.ProjectType.SYSTEM;
import static eu.numberfour.n4js.n4mf.ProjectType.TEST;
import static eu.numberfour.n4js.n4mf.resource.N4MFResourceDescriptionStrategy.getProjectId;
import static eu.numberfour.n4js.n4mf.resource.N4MFResourceDescriptionStrategy.getProjectType;
import static eu.numberfour.n4js.n4mf.ui.internal.N4MFActivator.EU_NUMBERFOUR_N4JS_N4MF_N4MF;
import static eu.numberfour.n4js.n4mf.ui.wizard.N4MFProjectInfo.IMPLEMENTATION_ID_PROP_NAME;
import static eu.numberfour.n4js.n4mf.ui.wizard.N4MFProjectInfo.IMPLEMENTED_PROJECTS_PROP_NAME;
import static eu.numberfour.n4js.n4mf.ui.wizard.N4MFProjectInfo.PROJECT_TYPE_PROP_NAME;
import static org.eclipse.jface.databinding.viewers.ViewersObservables.observeSingleSelection;
import static org.eclipse.jface.layout.GridDataFactory.fillDefaults;
import static org.eclipse.swt.SWT.BORDER;
import static org.eclipse.swt.SWT.CHECK;
import static org.eclipse.swt.SWT.FILL;
import static org.eclipse.swt.SWT.MULTI;
import static org.eclipse.swt.SWT.Modify;
import static org.eclipse.swt.SWT.READ_ONLY;
import static org.eclipse.xtext.util.Strings.toFirstUpper;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.beans.PojoProperties;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
import org.eclipse.xtext.resource.IResourceDescriptions;

import com.google.inject.Injector;

import eu.numberfour.n4js.n4mf.N4mfPackage;
import eu.numberfour.n4js.n4mf.ProjectType;
import eu.numberfour.n4js.n4mf.ui.internal.N4MFActivator;

/**
 * Wizard page for configuring a new N4 project.
 */
public class N4MFWizardNewProjectCreationPage extends WizardNewProjectCreationPage {

    private final N4MFProjectInfo projectInfo;

    /**
     * Creates a new wizard page to set up and create a new N4 project with the given project info model.
     *
     * @param projectInfo
     *            the project info model that will be used to initialize the new N4 project.
     */
    public N4MFWizardNewProjectCreationPage(final N4MFProjectInfo projectInfo) {
        super(N4MFWizardNewProjectCreationPage.class.getName());
        this.projectInfo = projectInfo;
        setTitle("N4JS Project");
        setDescription("Create a new N4JS project.");
    }

    @Override
    public boolean canFlipToNextPage() {
        // Only allow page flipping for test projects
        return isPageComplete() && TEST.equals(projectInfo.getProjectType());
    }

    @Override
    public void createControl(final Composite parent) {
        super.createControl(parent); // We need to create the UI controls from the parent class.

        final Composite control = (Composite) getControl();
        control.setLayout(GridLayoutFactory.fillDefaults().create());
        control.setLayoutData(fillDefaults().align(FILL, FILL).grab(true, true).create());

        final DataBindingContext dbc = new DataBindingContext();
        control.addDisposeListener(e -> dbc.dispose());

        final ComboViewer projectType = new ComboViewer(control, READ_ONLY);
        projectType.setLabelProvider(new ProjectTypeLabelProvider());
        projectType.setContentProvider(ArrayContentProvider.getInstance());
        projectType.getControl().setLayoutData(fillDefaults().grab(true, false).create());
        projectType.setInput(ProjectType.values());

        // A composite to hold the changing UI component (additional library project options / additional test project
        // options)
        final Composite changingComposite = new Composite(control, NONE);
        StackLayout changingStackLayout = new StackLayout();
        changingComposite.setLayout(changingStackLayout);
        changingComposite.setLayoutData(fillDefaults().align(FILL, FILL).grab(true, true).create());

        Composite defaultOptions = initDefaultOptionsUI(dbc, changingComposite);
        Composite libraryProjectOptionsGroup = initLibraryOptionsUI(dbc, changingComposite);
        Composite testProjectOptionsGroup = initTestProjectUI(dbc, changingComposite);

        initProjectTypeBinding(dbc, projectType);

        // Configure stack layout to show advanced options
        projectType.addPostSelectionChangedListener(e -> {
            switch (projectInfo.getProjectType()) {
            case LIBRARY:
                changingStackLayout.topControl = libraryProjectOptionsGroup;
                break;
            case TEST:
                changingStackLayout.topControl = testProjectOptionsGroup;
                break;
            default:
                changingStackLayout.topControl = defaultOptions;
            }
            changingComposite.layout(true);
            setPageComplete(validatePage());
        });

        // IDs from: org.eclipse.jdt.internal.ui.workingsets.IWorkingSetIDs.class
        createWorkingSetGroup((Composite) getControl(), null,
                new String[] { "org.eclipse.ui.resourceWorkingSetPage", "org.eclipse.jdt.ui.JavaWorkingSetPage",
                        "org.eclipse.jdt.internal.ui.OthersWorkingSet" }); // $NON-NLS-1$
        Dialog.applyDialogFont(getControl());

        dbc.updateTargets();

        setControl(control);
    }

    private Composite initDefaultOptionsUI(DataBindingContext dbc, Composite parent) {
        // A group for default options
        final Group defaultOptions = new Group(parent, NONE);
        defaultOptions.setLayout(GridLayoutFactory.fillDefaults().numColumns(1).create());

        final Button createGreeterFileButton = new Button(defaultOptions, CHECK);
        createGreeterFileButton.setText("Create a greeter file");

        initDefaultCreateGreeterBindings(dbc, createGreeterFileButton);

        return defaultOptions;
    }

    private Composite initLibraryOptionsUI(DataBindingContext dbc, Composite parent) {
        // Additional library project options
        final Group libraryProjectOptionsGroup = new Group(parent, NONE);
        libraryProjectOptionsGroup
                .setLayout(GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).create());

        emptyPlaceholder(libraryProjectOptionsGroup);

        final Button createGreeterFileButton = new Button(libraryProjectOptionsGroup, CHECK);
        createGreeterFileButton.setText("Create a greeter file");
        createGreeterFileButton.setLayoutData(GridDataFactory.fillDefaults().create());

        new Label(libraryProjectOptionsGroup, SWT.NONE).setText("Implementation ID:");
        final Text implementationIdText = new Text(libraryProjectOptionsGroup, BORDER);
        implementationIdText.setLayoutData(fillDefaults().align(FILL, SWT.CENTER).grab(true, false).create());

        final Label implementedProjectsLabel = new Label(libraryProjectOptionsGroup, SWT.NONE);
        implementedProjectsLabel.setText("Implemented projects:");
        implementedProjectsLabel
                .setLayoutData(GridDataFactory.fillDefaults().grab(false, true).align(SWT.LEFT, SWT.TOP).create());

        final ListViewer apiViewer = new ListViewer(libraryProjectOptionsGroup, BORDER | MULTI);
        apiViewer.getControl().setLayoutData(fillDefaults().align(FILL, FILL).grab(true, true).span(1, 1).create());
        apiViewer.setContentProvider(ArrayContentProvider.getInstance());
        apiViewer.setInput(getAvailableApiProjectIds());

        initApiViewerBinding(dbc, apiViewer);
        initImplementationIdBinding(dbc, implementationIdText);
        initDefaultCreateGreeterBindings(dbc, createGreeterFileButton);

        // Invalidate on change
        apiViewer.addSelectionChangedListener(e -> {
            setPageComplete(validatePage());
        });
        // Invalidate on change
        implementationIdText.addModifyListener(e -> {
            setPageComplete(validatePage());
        });

        return libraryProjectOptionsGroup;
    }

    /** Create an empty placeholder control in parent */
    private static Control emptyPlaceholder(Composite parent) {
        return new Label(parent, NONE);
    }

    private Composite initTestProjectUI(DataBindingContext dbc, Composite parent) {
        // Additional test project options
        final Group testProjectOptionsGroup = new Group(parent, NONE);
        testProjectOptionsGroup.setLayout(GridLayoutFactory.fillDefaults().numColumns(1).create());

        final Button createTestGreeterFileButton = new Button(testProjectOptionsGroup, CHECK);
        createTestGreeterFileButton.setText("Create a test project greeter file");

        final Button addNormalSourceFolderButton = new Button(testProjectOptionsGroup, CHECK);
        addNormalSourceFolderButton.setText("Also create a non-test source folder");

        Label nextPageHint = new Label(testProjectOptionsGroup, NONE);
        nextPageHint.setText("The projects which should be tested can be selected on the next page");
        nextPageHint.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND));

        initTestProjectBinding(dbc, addNormalSourceFolderButton, createTestGreeterFileButton);

        return testProjectOptionsGroup;
    }

    @Override
    protected boolean validatePage() {
        final boolean valid = super.validatePage();

        if (valid) {

            String errorMsg = null;
            final String projectName = getProjectName();

            if (LIBRARY.equals(projectInfo.getProjectType())) {

                final String implementationId = projectInfo.getImplementationId();

                // Implementation ID is optional
                if (!isNullOrEmpty(implementationId)) {

                    final List<String> implementedApis = projectInfo.getImplementedProjects();
                    if (null == implementedApis || implementedApis.isEmpty()) {
                        errorMsg = "One or more API project should be selected for implementation when the implementation ID is specified.";
                    }

                    if (BREAKING_WHITESPACE.matchesAnyOf(implementationId)) {
                        errorMsg = "Implementation ID should not contain any whitespace characters.";
                    }

                    final char leadincChar = implementationId.charAt(0);
                    if (!is('_').or(JAVA_LETTER).matches(leadincChar)) {
                        errorMsg = "Implementation ID should start either an upper or a lower case character "
                                + "from the Latin alphabet or with the underscore character.";
                    }
                }
            }

            if (SYSTEM.equals(projectInfo.getProjectType())) {
                errorMsg = "Project type 'System' is deprecated and will be removed soon. Use either 'API' or 'Library' instead.";
            }

            if (isNullOrEmpty(projectName)) {
                errorMsg = "Project name should be specified.";
            }

            if (BREAKING_WHITESPACE.matchesAnyOf(projectName)) {
                errorMsg = "Project name should not contain any whitespace characters.";
            }

            final char leadincChar = projectName.charAt(0);
            if (!is('_').or(JAVA_LETTER).matches(leadincChar)) {
                errorMsg = "Project name should start either an upper or a lower case character "
                        + "from the Latin alphabet or with the underscore character.";
            }

            setErrorMessage(errorMsg);
            if (null == errorMsg) {
                updateModel();
            }
            return null == errorMsg;
        }

        return valid;
    }

    /**
     * Accessible reader from outside. Call before using the project info.
     */
    public void updateSelectedWorkingSets() {
        projectInfo.setSelectedWorkingSets(getSelectedWorkingSets());
    }

    private void updateModel() {
        projectInfo.setProjectName(getProjectName());
        if (useDefaults()) {
            projectInfo.setProjectLocation(null);
        } else {
            projectInfo.setProjectLocation(getLocationPath());
        }
    }

    private void initDefaultCreateGreeterBindings(DataBindingContext dbc, Button createGreeterFileButton) {
        // Bind the "create greeter file"-checkbox
        dbc.bindValue(WidgetProperties.selection().observe(createGreeterFileButton), BeanProperties
                .value(N4MFProjectInfo.class, N4MFProjectInfo.CREATE_GREETER_FILE_PROP_NAME).observe(projectInfo));
    }

    private void initTestProjectBinding(DataBindingContext dbc, Button addNormalSourceFolderButton,
            Button createTestGreeterFileButton) {
        // Bind the "normal source folder"-checkbox
        dbc.bindValue(WidgetProperties.selection().observe(addNormalSourceFolderButton),
                PojoProperties
                        .value(N4MFProjectInfo.class, N4MFProjectInfo.ADDITIONAL_NORMAL_SOURCE_FOLDER_PROP_NAME)
                        .observe(projectInfo));

        // Bind the "Create greeter file"-checkbox
        dbc.bindValue(WidgetProperties.selection().observe(createTestGreeterFileButton), BeanProperties
                .value(N4MFProjectInfo.class, N4MFProjectInfo.CREATE_GREETER_FILE_PROP_NAME).observe(projectInfo));
    }

    private void initProjectTypeBinding(final DataBindingContext dbc, final ComboViewer projectType) {
        dbc.bindValue(observeSingleSelection(projectType),
                PojoProperties.value(N4MFProjectInfo.class, PROJECT_TYPE_PROP_NAME).observe(projectInfo));
    }

    private void initImplementationIdBinding(final DataBindingContext dbc, final Text text) {
        dbc.bindValue(WidgetProperties.text(Modify).observe(text),
                PojoProperties.value(N4MFProjectInfo.class, IMPLEMENTATION_ID_PROP_NAME).observe(projectInfo));
    }

    private void initApiViewerBinding(DataBindingContext dbc, ListViewer apiViewer) {
        dbc.bindList(ViewersObservables.observeMultiSelection(apiViewer),
                PojoProperties.list(N4MFProjectInfo.class, IMPLEMENTED_PROJECTS_PROP_NAME).observe(projectInfo));
    }

    private Collection<String> getAvailableApiProjectIds() {
        final Collection<String> distinctIds = from(
                getResourceDescriptions().getExportedObjectsByType(N4mfPackage.eINSTANCE.getProjectDescription()))
                        .filter(desc -> API.equals(getProjectType(desc))).transform(desc -> getProjectId(desc))
                        .filter(id -> null != id).toSet();
        final List<String> ids = newArrayList(distinctIds);
        Collections.sort(ids);
        return ids;
    }

    private IResourceDescriptions getResourceDescriptions() {
        return getInjector().getInstance(IResourceDescriptions.class);
    }

    private Injector getInjector() {
        return N4MFActivator.getInstance().getInjector(EU_NUMBERFOUR_N4JS_N4MF_N4MF);
    }

    /**
     * Label provider for converting the {@link ProjectType} literal into a human readable string.
     */
    private static final class ProjectTypeLabelProvider extends LabelProvider {
        @Override
        public String getText(final Object element) {
            if (API.equals(element)) {
                return API.getLiteral();
            }
            if (SYSTEM.equals(element)) {
                return new StringBuilder(getDefaultText(element)).append(" [Deprecated]").toString();
            }
            return getDefaultText(element);
        }

        private String getDefaultText(final Object element) {
            return toFirstUpper(nullToEmpty(super.getText(element)).replaceAll("_", " ").toLowerCase());
        }
    }

}