com.amazonaws.eclipse.lambda.project.wizard.util.LambdaFunctionGroup.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.eclipse.lambda.project.wizard.util.LambdaFunctionGroup.java

Source

/*
 * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.amazonaws.eclipse.lambda.project.wizard.util;

import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newCheckbox;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newCombo;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newControlDecoration;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newFillingLabel;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newGroup;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newLink;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newText;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaHandlerType.REQUEST_HANDLER;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaHandlerType.STREAM_REQUEST_HANDLER;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaFunctionWizardDataModel.P_CUSTOM_HANDLER_INPUT_TYPE;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaFunctionWizardDataModel.P_HANDLER_CLASS_NAME;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaFunctionWizardDataModel.P_HANDLER_OUTPUT_TYPE;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaFunctionWizardDataModel.P_HANDLER_PACKAGE_NAME;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaFunctionWizardDataModel.P_HANDLER_TYPE;
import static com.amazonaws.eclipse.lambda.project.wizard.model.LambdaFunctionWizardDataModel.P_SHOW_README_FILE;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Iterator;

import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer;
import org.eclipse.jdt.internal.ui.text.SimpleJavaSourceViewerConfiguration;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaTextTools;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
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.Link;
import org.eclipse.swt.widgets.Text;

import com.amazonaws.eclipse.databinding.BooleanValidator;
import com.amazonaws.eclipse.databinding.ChainValidator;
import com.amazonaws.eclipse.databinding.DecorationChangeListener;
import com.amazonaws.eclipse.databinding.NotEmptyValidator;
import com.amazonaws.eclipse.lambda.LambdaPlugin;
import com.amazonaws.eclipse.lambda.UrlConstants;
import com.amazonaws.eclipse.lambda.project.template.CodeTemplateManager;
import com.amazonaws.eclipse.lambda.project.wizard.model.LambdaHandlerType;
import com.amazonaws.eclipse.lambda.project.wizard.model.LambdaFunctionWizardDataModel;
import com.amazonaws.eclipse.lambda.project.wizard.model.PredefinedHandlerInputType;
import com.amazonaws.eclipse.lambda.project.wizard.page.validator.ValidPackageNameValidator;
import com.amazonaws.eclipse.sdk.ui.JavaSdkManager;

import freemarker.template.Template;

@SuppressWarnings("restriction")
public class LambdaFunctionGroup {

    private WizardPage parentWizard;

    private final LambdaFunctionWizardDataModel dataModel;
    private final DataBindingContext bindingContext;
    private final AggregateValidationStatus aggregateValidationStatus;

    /* Function handler section */
    private Text handlerPackageText;
    private ControlDecoration handlerPackageTextDecoration;
    private ISWTObservableValue handlerPackageTextObservable;

    private Text handlerClassText;
    private ControlDecoration handlerClassTextDecoration;
    private ISWTObservableValue handlerClassTextObservable;

    private Combo handlerTypeCombo;
    private Link handlerTypeDescriptionLink;
    private ISWTObservableValue handlerTypeComboObservable;

    private Combo predefinedHandlerInputCombo;

    private Text customHandlerInputTypeText;
    private ControlDecoration customHandlerInputTypeTextDecoration;
    private ISWTObservableValue customHandlerInputTypeTextObservable;
    private IObservableValue enableCustomHandlerInputTypeValidation = new WritableValue();

    private IObservableValue enableSdkInstalledValidation = new WritableValue();
    private IObservableValue sdkInstalledObservable = new WritableValue();

    private Text handlerOutputTypeText;
    private ControlDecoration handlerOutputTypeTextDecoration;
    private ISWTObservableValue handlerOutputTypeTextObservable;

    private JavaSourceViewer sourcePreview;
    private Document sourcePreviewDocument;
    private final Template handlerTemplate;
    private final Template streamHandlerTemplate;

    private static String CUSTOM_INPUT_TYPE_COMBO_TEXT = "Custom";
    private static Object CUSTOM_INPUT_TYPE_COMBO_DATA = new Object();

    /* Check box to opt-out showing README.html */
    private Button showReadmeFileCheckbox;
    private ISWTObservableValue showReadmeFileCheckboxObservable;

    private Composite parentComposite;
    private Group group;
    private Composite inputComposite;

    public LambdaFunctionGroup(final WizardPage parentWizard, final LambdaFunctionWizardDataModel dataModel) {
        this.parentWizard = parentWizard;
        this.dataModel = dataModel;
        this.bindingContext = new DataBindingContext();
        this.aggregateValidationStatus = new AggregateValidationStatus(bindingContext,
                AggregateValidationStatus.MAX_SEVERITY);

        this.handlerTemplate = CodeTemplateManager.getInstance().getHandlerClassTemplate();
        this.streamHandlerTemplate = CodeTemplateManager.getInstance().getStreamHandlderClassTemplate();
    }

    // This method is supposed to be called once and before creating the controls
    public void init(Composite composite) {
        parentComposite = composite;
        group = newGroup(composite, "Lambda Function Handler");
        GridLayout groupLayout = new GridLayout(1, true);
        groupLayout.marginWidth = 15;
        group.setLayout(groupLayout);

        String description = "Each Lambda function must specify a handler class "
                + "which the service will use as the entry point to begin execution.";
        setItalicFont(newLink(group, UrlConstants.webLinkListener,
                description + " <a href=\"" + UrlConstants.LAMBDA_EXECUTION_ROLE_DOC_URL
                        + "\">Learn more</a> about Lambda Java function handler.",
                1));

        inputComposite = new Composite(group, SWT.NONE);
        inputComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
        GridLayout inputCompLayout = new GridLayout(3, true);
        inputCompLayout.marginHeight = 10;
        inputCompLayout.marginWidth = 10;
        inputComposite.setLayout(inputCompLayout);
    }

    public void createPackageNameControl() {
        // create package name control
        newFillingLabel(inputComposite, "Package Name:");
        handlerPackageText = newText(inputComposite, "", 2);
        handlerPackageTextDecoration = newControlDecoration(handlerPackageText, "");

        // bind package name control to data model
        handlerPackageTextObservable = SWTObservables.observeText(handlerPackageText, SWT.Modify);
        bindingContext.bindValue(handlerPackageTextObservable,
                PojoObservables.observeValue(dataModel, P_HANDLER_PACKAGE_NAME));

        // bind validation of package name
        ChainValidator<String> handlerPackageValidator = new ChainValidator<String>(handlerPackageTextObservable,
                new ValidPackageNameValidator("Please provide a valid package name for the handler class"));
        bindingContext.addValidationStatusProvider(handlerPackageValidator);
        new DecorationChangeListener(handlerPackageTextDecoration, handlerPackageValidator.getValidationStatus());
    }

    public void createClassNameControl() {
        // create Lambda function handler class name control
        newFillingLabel(inputComposite, "Class Name:");
        handlerClassText = newText(inputComposite, "", 2);
        handlerClassTextDecoration = newControlDecoration(handlerClassText, "");

        // bind Lambda function handler class name control to data model
        handlerClassTextObservable = SWTObservables.observeText(handlerClassText, SWT.Modify);
        bindingContext.bindValue(handlerClassTextObservable,
                PojoObservables.observeValue(dataModel, P_HANDLER_CLASS_NAME));

        // bind validation of class name
        ChainValidator<String> handlerClassValidator = new ChainValidator<String>(handlerClassTextObservable,
                new NotEmptyValidator("Please provide a valid class name for the handler"));
        bindingContext.addValidationStatusProvider(handlerClassValidator);
        new DecorationChangeListener(handlerClassTextDecoration, handlerClassValidator.getValidationStatus());
    }

    public void createLambdaHandlerControl() {
        // create handler type control
        newFillingLabel(inputComposite, "Handler Type");
        handlerTypeCombo = createHandlerTypeCombo(inputComposite, 1);
        handlerTypeDescriptionLink = newLink(inputComposite, UrlConstants.webLinkListener, "", 1);
        setItalicFont(handlerTypeDescriptionLink);

        // create input type control
        newFillingLabel(inputComposite, "Input Type:");
        predefinedHandlerInputCombo = createPredefinedHandlerInputTypeCombo(inputComposite, 1);
        customHandlerInputTypeText = newText(inputComposite, "", 1);
        customHandlerInputTypeTextDecoration = newControlDecoration(customHandlerInputTypeText, "");

        // create output type control
        newFillingLabel(inputComposite, "Output Type:");
        handlerOutputTypeText = newText(inputComposite, "", 2);
        handlerOutputTypeTextDecoration = newControlDecoration(handlerOutputTypeText, "");

        // bind handler type control to data model
        handlerTypeComboObservable = SWTObservables.observeText(handlerTypeCombo);
        bindingContext.bindValue(handlerTypeComboObservable,
                PojoObservables.observeValue(dataModel, P_HANDLER_TYPE));

        // bind input type control to data model
        customHandlerInputTypeTextObservable = SWTObservables.observeText(customHandlerInputTypeText, SWT.Modify);
        bindingContext.bindValue(customHandlerInputTypeTextObservable,
                PojoObservables.observeValue(dataModel, P_CUSTOM_HANDLER_INPUT_TYPE));

        // bind output type control to data model
        handlerOutputTypeTextObservable = SWTObservables.observeText(handlerOutputTypeText, SWT.Modify);
        bindingContext.bindValue(handlerOutputTypeTextObservable,
                PojoObservables.observeValue(dataModel, P_HANDLER_OUTPUT_TYPE));

        // bind validation of input type
        ChainValidator<String> customHandlerInputChainValidator = new ChainValidator<String>(
                customHandlerInputTypeTextObservable, enableCustomHandlerInputTypeValidation, //enabler
                new NotEmptyValidator("Please provide a valid input type"));
        bindingContext.addValidationStatusProvider(customHandlerInputChainValidator);
        new DecorationChangeListener(customHandlerInputTypeTextDecoration,
                customHandlerInputChainValidator.getValidationStatus());

        // bind validation of output type
        ChainValidator<String> handlerOutputChainValidator = new ChainValidator<String>(
                handlerOutputTypeTextObservable, new NotEmptyValidator("Please provide a valid output type"));
        bindingContext.addValidationStatusProvider(handlerOutputChainValidator);
        new DecorationChangeListener(handlerOutputTypeTextDecoration,
                handlerOutputChainValidator.getValidationStatus());

        // bind validation of SDK installed
        ChainValidator<Boolean> sdkInstalledValidator = new ChainValidator<Boolean>(sdkInstalledObservable,
                enableSdkInstalledValidation, // enabler
                new BooleanValidator("The selected input type requires the AWS Java SDK dependency. "
                        + "Please install the SDK first "
                        + "(Window -> Preference -> AWS Toolkit -> AWS SDK for Java) " + "and then retry."));
        bindingContext.addValidationStatusProvider(sdkInstalledValidator);
    }

    public void createSeparator() {
        Label separator = new Label(group, SWT.HORIZONTAL | SWT.SEPARATOR);
        separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    }

    private Combo createHandlerTypeCombo(Composite composite, int colspan) {
        final Combo combo = newCombo(composite, 1);

        for (LambdaHandlerType handlerType : LambdaHandlerType.values()) {
            combo.add(handlerType.getName());
            combo.setData(handlerType.getName(), handlerType);
        }

        combo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onHandlerTypeSelectionChange();
            }
        });
        return combo;
    }

    private void onHandlerTypeSelectionChange() {
        final String handlerType = handlerTypeCombo.getText();
        final Object handlerTypeData = handlerTypeCombo.getData(handlerType);

        if (STREAM_REQUEST_HANDLER == handlerTypeData) {
            customHandlerInputTypeText.setEnabled(false);
            enableCustomHandlerInputTypeValidation.setValue(false);

            handlerOutputTypeText.setEnabled(false);
            predefinedHandlerInputCombo.setEnabled(false);

            handlerTypeDescriptionLink.setText(createHandlerTypeDescriptionLink(STREAM_REQUEST_HANDLER));

        } else if (REQUEST_HANDLER == handlerTypeData) {

            String selectedText = predefinedHandlerInputCombo.getText();
            Object selectedData = predefinedHandlerInputCombo.getData(selectedText);

            boolean customHandlerInputTypeTextEnabled = selectedData == CUSTOM_INPUT_TYPE_COMBO_DATA;
            customHandlerInputTypeText.setEnabled(customHandlerInputTypeTextEnabled);
            enableCustomHandlerInputTypeValidation.setValue(customHandlerInputTypeTextEnabled);

            handlerOutputTypeText.setEnabled(true);
            predefinedHandlerInputCombo.setEnabled(true);

            handlerTypeDescriptionLink.setText(createHandlerTypeDescriptionLink(REQUEST_HANDLER));

        } else {
            LambdaHandlerType lambdaHandlerType = (LambdaHandlerType) handlerTypeData;
            MessageDialog.openInformation(parentWizard.getShell(), "Unsupported handler type combo selection.",
                    "The handler type " + handlerType
                            + " is not yet supported in the toolkit! For more information, see "
                            + lambdaHandlerType.getDocUrl() + ".");
            handlerTypeCombo.select(0);
            onHandlerTypeSelectionChange();
        }
    }

    /** Return the descriptive words for the specific Lambda Handler type. */
    private String createHandlerTypeDescriptionLink(LambdaHandlerType handlerType) {
        return "<a href=\"" + handlerType.getDocUrl() + "\">Learn more</a> about handlers.";
    }

    private Combo createPredefinedHandlerInputTypeCombo(Composite composite, int colspan) {

        final Combo combo = newCombo(composite, 1);

        for (PredefinedHandlerInputType type : PredefinedHandlerInputType.values()) {
            combo.add(type.getDisplayName());
            combo.setData(type.getDisplayName(), type);
        }

        combo.add(CUSTOM_INPUT_TYPE_COMBO_TEXT);
        combo.setData(CUSTOM_INPUT_TYPE_COMBO_TEXT, CUSTOM_INPUT_TYPE_COMBO_DATA);

        combo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onPredefinedHandlerInputTypeComboSelectionChange();
            }
        });

        return combo;
    }

    private void onPredefinedHandlerInputTypeComboSelectionChange() {
        String selectedText = predefinedHandlerInputCombo.getText();
        Object selectedData = predefinedHandlerInputCombo.getData(selectedText);

        if (selectedData == CUSTOM_INPUT_TYPE_COMBO_DATA) {
            customHandlerInputTypeText.setEnabled(true);
            enableCustomHandlerInputTypeValidation.setValue(true);
            dataModel.setPredefinedHandlerInputType(null);

            enableSdkInstalledValidation.setValue(false);

        } else if (selectedData instanceof PredefinedHandlerInputType) {
            customHandlerInputTypeText.setEnabled(false);
            enableCustomHandlerInputTypeValidation.setValue(false);

            PredefinedHandlerInputType type = (PredefinedHandlerInputType) selectedData;
            dataModel.setPredefinedHandlerInputType(type);

            enableSdkInstalledValidation.setValue(type.requireSdkDependency());

        } else {
            LambdaPlugin.getDefault().warn("Unknown combo selection " + selectedText, null);
        }

    }

    public void createHandlerSourcePreview() {

        newFillingLabel(group, "Preview:", 1);

        sourcePreviewDocument = new Document("");
        IPreferenceStore javaPluginPrefStore = JavaPlugin.getDefault().getCombinedPreferenceStore();

        sourcePreview = new JavaSourceViewer(group, null, null, false, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER,
                javaPluginPrefStore);
        sourcePreview.setEditable(false);
        sourcePreview.getTextWidget().setFont(JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT));

        // Setting up Java Syntax Highlight
        JavaTextTools tools = JavaPlugin.getDefault().getJavaTextTools();
        tools.setupJavaDocumentPartitioner(sourcePreviewDocument, IJavaPartitions.JAVA_PARTITIONING);
        SimpleJavaSourceViewerConfiguration sourceConfig = new SimpleJavaSourceViewerConfiguration(
                tools.getColorManager(), javaPluginPrefStore, null, IJavaPartitions.JAVA_PARTITIONING, true);
        sourcePreview.configure(sourceConfig);

        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        gridData.horizontalSpan = 1;
        gridData.heightHint = 50;
        sourcePreview.getTextWidget().setLayoutData(gridData);
        sourcePreview.setDocument(sourcePreviewDocument);

        linkPreviewWithHandlerConfigInput();
    }

    private void linkPreviewWithHandlerConfigInput() {

        dataModel.addPropertyChangeListener(new PropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent event) {

                Template template = dataModel.isUseStreamHandler() ? streamHandlerTemplate : handlerTemplate;
                Object freeMarkerDataModel = dataModel.isUseStreamHandler()
                        ? dataModel.collectStreamHandlerTemplateData()
                        : dataModel.collectHandlerTemplateData();

                StringWriter sw = new StringWriter();
                try {
                    template.process(freeMarkerDataModel, sw);
                    sw.flush();
                } catch (Exception e) {
                    LambdaPlugin.getDefault().reportException("Failed to generate handler source preview", e);
                }
                try {
                    sw.close();
                } catch (IOException ignored) {
                }

                String source = sw.toString();
                if (sourcePreviewDocument != null) {
                    sourcePreviewDocument.set(source);
                }
                if (sourcePreview != null) {
                    sourcePreview.getTextWidget().setRedraw(true);
                }
            }
        });
    }

    public void createShowReadmeFileCheckBox() {
        showReadmeFileCheckbox = newCheckbox(parentComposite, "Show README guide after creating the project", 1);

        showReadmeFileCheckboxObservable = SWTObservables.observeSelection(showReadmeFileCheckbox);
        bindingContext.bindValue(showReadmeFileCheckboxObservable,
                PojoObservables.observeValue(dataModel, P_SHOW_README_FILE));
    }

    public void initializeValidators(IChangeListener listener) {
        aggregateValidationStatus.addChangeListener(listener);
    }

    public void runHandlerValidators() {
        Iterator<?> iterator = bindingContext.getBindings().iterator();
        while (iterator.hasNext()) {
            Binding binding = (Binding) iterator.next();
            binding.updateTargetToModel();
        }
    }

    public void initializeDefaults() {
        if (handlerPackageTextObservable != null) {
            handlerPackageTextObservable.setValue("");
        }
        if (handlerClassTextObservable != null) {
            handlerClassTextObservable.setValue("LambdaFunctionHandler");
        }
        if (customHandlerInputTypeTextObservable != null) {
            customHandlerInputTypeTextObservable.setValue("Object");
        }
        if (handlerOutputTypeTextObservable != null) {
            handlerOutputTypeTextObservable.setValue("Object");
        }

        if (handlerTypeCombo != null) {
            handlerTypeCombo.select(0);
            onHandlerTypeSelectionChange();
        }

        if (predefinedHandlerInputCombo != null) {
            predefinedHandlerInputCombo.select(0);
            onPredefinedHandlerInputTypeComboSelectionChange();
        }

        if (sdkInstalledObservable != null)
            sdkInstalledObservable.setValue(checkSdkInstalled());

        if (showReadmeFileCheckboxObservable != null) {
            showReadmeFileCheckboxObservable.setValue(LambdaPlugin.getDefault().getPreferenceStore()
                    .getBoolean(LambdaPlugin.PREF_K_SHOW_README_AFTER_CREATE_NEW_PROJECT));
        }
    }

    private boolean checkSdkInstalled() {
        return JavaSdkManager.getInstance().getDefaultSdkInstall() != null;
    }

    public IStatus getHandlerInfoValidationStatus() {
        if (aggregateValidationStatus == null) {
            return null;
        }

        Object value = aggregateValidationStatus.getValue();
        if (!(value instanceof IStatus))
            return null;
        return (IStatus) value;
    }

    private Font italicFont;

    private void setItalicFont(Control control) {
        FontData[] fontData = control.getFont().getFontData();
        for (FontData fd : fontData) {
            fd.setStyle(SWT.ITALIC);
        }
        italicFont = new Font(Display.getDefault(), fontData);
        control.setFont(italicFont);
    }

    public void dispose() {
        if (italicFont != null) {
            italicFont.dispose();
        }
    }
}