com.google.cloud.tools.eclipse.appengine.newproject.maven.MavenAppEngineStandardWizardPage.java Source code

Java tutorial

Introduction

Here is the source code for com.google.cloud.tools.eclipse.appengine.newproject.maven.MavenAppEngineStandardWizardPage.java

Source

/*
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.cloud.tools.eclipse.appengine.newproject.maven;

import com.google.cloud.tools.eclipse.appengine.libraries.model.Library;
import com.google.cloud.tools.eclipse.appengine.newproject.JavaPackageValidator;
import com.google.cloud.tools.eclipse.appengine.ui.AppEngineImages;
import com.google.cloud.tools.eclipse.appengine.ui.AppEngineLibrariesSelectorGroup;
import com.google.cloud.tools.eclipse.usagetracker.AnalyticsEvents;
import com.google.cloud.tools.eclipse.usagetracker.AnalyticsPingManager;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.CharMatcher;
import java.text.MessageFormat;
import java.util.List;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
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.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

/**
 * UI to collect all information necessary to create a new Maven-based App Engine Standard Java
 * project.
 */
public class MavenAppEngineStandardWizardPage extends WizardPage {

    private String defaultVersion = "0.1.0-SNAPSHOT"; //$NON-NLS-1$

    private Button useDefaults;
    private Text locationField;
    private Button locationBrowseButton;
    @VisibleForTesting
    Text groupIdField;
    private Text artifactIdField;
    private Text versionField;
    @VisibleForTesting
    Text javaPackageField;
    private AppEngineLibrariesSelectorGroup appEngineLibrariesSelectorGroup;

    private boolean canFlipPage;

    /** True if we should auto-generate the javaPackageField from the provided groupId */
    @VisibleForTesting
    boolean autoGeneratePackageName = true;

    /** True if we're programmatically setting javaPackageField with an auto-generated value */
    private boolean javaPackageProgrammaticUpdate = false;

    public MavenAppEngineStandardWizardPage() {
        super("basicNewProjectPage"); //$NON-NLS-1$
        setTitle(Messages.getString("WIZARD_TITLE")); //$NON-NLS-1$
        setDescription(Messages.getString("WIZARD_DESCRIPTION")); //$NON-NLS-1$
        setImageDescriptor(AppEngineImages.googleCloudPlatform(32));

        canFlipPage = false;
    }

    @Override
    public void createControl(Composite parent) {
        AnalyticsPingManager.getInstance().sendPing(AnalyticsEvents.APP_ENGINE_NEW_PROJECT_WIZARD,
                AnalyticsEvents.APP_ENGINE_NEW_PROJECT_WIZARD_TYPE,
                AnalyticsEvents.APP_ENGINE_NEW_PROJECT_WIZARD_TYPE_MAVEN, parent.getShell());

        Composite container = new Composite(parent, SWT.NONE);
        GridLayoutFactory.swtDefaults().numColumns(2).applyTo(container);
        setControl(container);

        createLocationArea(container);
        createMavenCoordinatesArea(container);
        createAppEngineProjectDetailsArea(container);
        appEngineLibrariesSelectorGroup = new AppEngineLibrariesSelectorGroup(container);

        Dialog.applyDialogFont(container);
    }

    /** Create UI for specifying the generated location area */
    private void createLocationArea(Composite container) {
        ModifyListener pageValidator = new PageValidator();

        Group locationGroup = new Group(container, SWT.NONE);
        locationGroup.setText(Messages.getString("LOCATION_GROUP_TEXT")); //$NON-NLS-1$
        GridDataFactory.fillDefaults().span(2, 1).applyTo(locationGroup);
        GridLayoutFactory.swtDefaults().numColumns(3).applyTo(locationGroup);

        useDefaults = new Button(locationGroup, SWT.CHECK);
        GridDataFactory.defaultsFor(useDefaults).span(3, 1).applyTo(useDefaults);
        useDefaults.setText(Messages.getString("CREATE_PROJECT_IN_WORKSPACE")); //$NON-NLS-1$
        useDefaults.setSelection(true);

        Label locationLabel = new Label(locationGroup, SWT.NONE);
        locationLabel.setText(Messages.getString("LOCATION_LABEL")); //$NON-NLS-1$
        locationLabel.setToolTipText(Messages.getString("LOCATION_TOOL_TIP")); //$NON-NLS-1$

        locationField = new Text(locationGroup, SWT.BORDER);
        GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(locationField);
        locationField.addModifyListener(pageValidator);
        locationField.setEnabled(false);

        locationBrowseButton = new Button(locationGroup, SWT.PUSH);
        locationBrowseButton.setText(Messages.getString("BROWSE_BUTTON_LABEL")); //$NON-NLS-1$
        locationBrowseButton.setEnabled(false);
        locationBrowseButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                openLocationDialog();
            }
        });
        useDefaults.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                locationField.setEnabled(!useDefaults.getSelection());
                locationBrowseButton.setEnabled(!useDefaults.getSelection());
                checkFlipToNext();
            }
        });
    }

    /** Create UI for specifying desired Maven Coordinates */
    private void createMavenCoordinatesArea(Composite container) {
        ModifyListener pageValidator = new PageValidator();

        Group mavenCoordinatesGroup = new Group(container, SWT.NONE);
        mavenCoordinatesGroup.setText(Messages.getString("MAVEN_PROJECT_COORDINATES")); //$NON-NLS-1$
        GridDataFactory.defaultsFor(mavenCoordinatesGroup).span(2, 1).applyTo(mavenCoordinatesGroup);
        GridLayoutFactory.swtDefaults().numColumns(2).applyTo(mavenCoordinatesGroup);

        Label groupIdLabel = new Label(mavenCoordinatesGroup, SWT.NONE);
        groupIdLabel.setText(Messages.getString("GROUP_ID")); //$NON-NLS-1$
        groupIdField = new Text(mavenCoordinatesGroup, SWT.BORDER);
        GridDataFactory.defaultsFor(groupIdField).align(SWT.FILL, SWT.CENTER).applyTo(groupIdField);
        groupIdField.addModifyListener(pageValidator);
        groupIdField.addModifyListener(new AutoPackageNameSetterOnGroupIdChange());

        Label artifactIdLabel = new Label(mavenCoordinatesGroup, SWT.NONE);
        artifactIdLabel.setText(Messages.getString("ARTIFACT_ID")); //$NON-NLS-1$
        artifactIdField = new Text(mavenCoordinatesGroup, SWT.BORDER);
        GridDataFactory.defaultsFor(artifactIdField).align(SWT.FILL, SWT.CENTER).applyTo(artifactIdField);
        artifactIdField.addModifyListener(pageValidator);

        Label versionLabel = new Label(mavenCoordinatesGroup, SWT.NONE);
        versionLabel.setText(Messages.getString("ARTIFACT_VERSION")); //$NON-NLS-1$
        versionField = new Text(mavenCoordinatesGroup, SWT.BORDER);
        versionField.setText(defaultVersion);
        GridDataFactory.defaultsFor(versionField).align(SWT.FILL, SWT.CENTER).applyTo(versionField);
        versionField.addModifyListener(pageValidator);
    }

    /** Create UI for specifying App Engine project details */
    private void createAppEngineProjectDetailsArea(Composite container) {
        ModifyListener pageValidator = new PageValidator();

        // Java package name
        Label packageNameLabel = new Label(container, SWT.NONE);
        packageNameLabel.setText(Messages.getString("JAVA_PACKAGE_LABEL")); //$NON-NLS-1$
        javaPackageField = new Text(container, SWT.BORDER);
        GridData javaPackagePosition = new GridData(GridData.FILL_HORIZONTAL);
        javaPackagePosition.horizontalSpan = 2;
        javaPackageField.setLayoutData(javaPackagePosition);
        javaPackageField.addModifyListener(pageValidator);
        javaPackageField.addVerifyListener(new VerifyListener() {

            @Override
            public void verifyText(VerifyEvent event) {
                // if the user ever changes the package name field, then we never auto-generate again
                if (!javaPackageProgrammaticUpdate) {
                    autoGeneratePackageName = false;
                }
            }
        });
    }

    protected void openLocationDialog() {
        DirectoryDialog dialog = new DirectoryDialog(getShell());
        dialog.setText(Messages.getString("GENERATED_PROJECT_LOCATION")); //$NON-NLS-1$
        String location = dialog.open();
        if (location != null) {
            locationField.setText(location);
            checkFlipToNext();
        }
    }

    @Override
    public boolean canFlipToNextPage() {
        return canFlipPage;
    }

    protected void checkFlipToNext() {
        canFlipPage = validatePage();
        getContainer().updateButtons();
    }

    /**
     * Validate and report on the contents of this page
     *
     * @return true if valid, false if there is a problem
     */
    public boolean validatePage() {
        setMessage(null);
        setErrorMessage(null);

        // order here should match order of the UI fields

        String location = locationField.getText().trim();
        if (!useDefaults() && location.isEmpty()) {
            setMessage(Messages.getString("PROVIDE_LOCATION"), INFORMATION); //$NON-NLS-1$
            return false;
        }

        if (!validateMavenSettings()) {
            return false;
        }
        if (!validateGeneratedProjectLocation()) {
            return false;
        }
        if (!validateAppEngineProjectDetails()) {
            return false;
        }

        return true;
    }

    public List<Library> getSelectedLibraries() {
        return appEngineLibrariesSelectorGroup.getSelectedLibraries();
    }

    @Override
    public void dispose() {
        appEngineLibrariesSelectorGroup.dispose();
        super.dispose();
    }

    /**
     * Check that we won't overwrite an existing location. Expects a valid Maven Artifact ID.
     */
    private boolean validateGeneratedProjectLocation() {
        String artifactId = getArtifactId();
        IPath path = getLocationPath().append(artifactId);
        if (path.toFile().exists()) {
            String errorMessage = MessageFormat.format(Messages.getString("LOCATION_ALREADY_EXISTS"), path);
            setErrorMessage(errorMessage); //$NON-NLS-1$
            return false;
        }
        return true;
    }

    private boolean validateMavenSettings() {
        String groupId = getGroupId();
        if (groupId.isEmpty()) {
            setMessage(Messages.getString("PROVIDE_GROUP_ID"), INFORMATION); //$NON-NLS-1$
            return false;
        } else if (!MavenCoordinatesValidator.validateGroupId(groupId)) {
            setErrorMessage(MessageFormat.format(Messages.getString("ILLEGAL_GROUP_ID"), groupId)); //$NON-NLS-1$
            return false;
        }
        String artifactId = getArtifactId();
        if (artifactId.isEmpty()) {
            setMessage(Messages.getString("PROVIDE_ARTIFACT_ID"), INFORMATION); //$NON-NLS-1$
            return false;
        } else if (!MavenCoordinatesValidator.validateArtifactId(artifactId)) {
            setErrorMessage(Messages.getString("ILLEGAL_ARTIFACT_ID") + artifactId); //$NON-NLS-1$
            return false;
        }
        String version = getVersion();
        if (version.isEmpty()) {
            setMessage(Messages.getString("PROVIDE_VERSION"), INFORMATION); //$NON-NLS-1$
            return false;
        } else if (!MavenCoordinatesValidator.validateVersion(version)) {
            setErrorMessage(Messages.getString("ILLEGAL_VERSION") + version); //$NON-NLS-1$
            return false;
        }
        return true;
    }

    private boolean validateAppEngineProjectDetails() {
        String packageName = getPackageName();
        IStatus status = JavaPackageValidator.validate(packageName);
        if (!status.isOK()) {
            String details = status.getMessage() == null ? packageName : status.getMessage();
            String message = MessageFormat.format(Messages.getString("ILLEGAL_PACKAGE_NAME"), details); //$NON-NLS-1$
            setErrorMessage(message);
            return false;
        }

        return true;
    }

    /** Return the Maven group for the project */
    public String getGroupId() {
        return groupIdField.getText().trim();
    }

    /** Return the Maven artifact for the project */
    public String getArtifactId() {
        return artifactIdField.getText().trim();
    }

    /** Return the Maven version for the project */
    public String getVersion() {
        return versionField.getText().trim();
    }

    /**
     * If true, projects are generated into the workspace, otherwise placed into a specified location.
     */
    public boolean useDefaults() {
        return useDefaults.getSelection();
    }

    /** Return the package name for any example code */
    public String getPackageName() {
        return this.javaPackageField.getText();
    }

    /** Return the location where the project should be generated into */
    public IPath getLocationPath() {
        if (useDefaults()) {
            return ResourcesPlugin.getWorkspace().getRoot().getLocation();
        }
        return new Path(locationField.getText());
    }

    private final class PageValidator implements ModifyListener {
        @Override
        public void modifyText(ModifyEvent event) {
            checkFlipToNext();
        }
    }

    /**
     * Auto-fills {@link #javaPackageField} from the Group ID if the user has not explicitly chosen a
     * package name.
     */
    private final class AutoPackageNameSetterOnGroupIdChange implements ModifyListener {

        @Override
        public void modifyText(ModifyEvent event) {
            // getGroupId() trims whitespace, so we do the same to sync with the dialog validation error.
            String groupId = groupIdField.getText().trim();

            if (MavenCoordinatesValidator.validateGroupId(groupId)) {
                String newSuggestion = suggestPackageName(groupId);
                updatePackageField(newSuggestion);
            } else if (groupId.isEmpty()) {
                updatePackageField("");
            }
        }
    }

    private void updatePackageField(String newSuggestion) {
        if (autoGeneratePackageName) {
            javaPackageProgrammaticUpdate = true;
            javaPackageField.setText(newSuggestion);
            javaPackageProgrammaticUpdate = false;
        }
    }

    /**
     * Helper function returning a suggested package name based on groupId.
     * It does basic string filtering/manipulation, which does not completely eliminate
     * naming issues. However, users will be alerted of any errors in naming by
     * {@link #validatePage}.
     */
    @VisibleForTesting
    static String suggestPackageName(String groupId) {

        if (JavaPackageValidator.validate(groupId).isOK()) {
            return groupId;
        }

        // 1) Remove leading and trailing dots.
        // 2) Keep only word characters ([a-zA-Z_0-9]) and dots (escaping inside [] not necessary).
        // 3) Replace consecutive dots with a single dot.
        return CharMatcher.is('.').trimFrom(groupId).replaceAll("[^\\w.]", "").replaceAll("\\.+", "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
    }
}