org.springframework.ide.eclipse.gettingstarted.guides.wizard.GuideImportWizardModel.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ide.eclipse.gettingstarted.guides.wizard.GuideImportWizardModel.java

Source

/*******************************************************************************
 * Copyright (c) 2013 GoPivotal, Inc.
 * 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:
 * GoPivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.eclipse.gettingstarted.guides.wizard;

import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.springframework.ide.eclipse.gettingstarted.GettingStartedActivator;
import org.springframework.ide.eclipse.gettingstarted.content.BuildType;
import org.springframework.ide.eclipse.gettingstarted.content.CodeSet;
import org.springframework.ide.eclipse.gettingstarted.dashboard.WebDashboardPage;
import org.springframework.ide.eclipse.gettingstarted.guides.GettingStartedGuide;
import org.springframework.ide.eclipse.gettingstarted.importing.ImportConfiguration;
import org.springframework.ide.eclipse.gettingstarted.importing.ImportStrategy;
import org.springframework.ide.eclipse.gettingstarted.importing.ImportUtils;
import org.springframework.ide.eclipse.gettingstarted.util.UIThreadDownloadDisallowed;
import org.springsource.ide.eclipse.commons.core.util.ExceptionUtil;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;

/**
 * Core counterpart of GuideImportWizard (essentially this is a 'model' for the wizard
 * UI.
 * 
 * @author Kris De Volder
 */
public class GuideImportWizardModel {

    //TODO: when guide selection is changed and swithched back... elements get 
    //  codeset names may get deselected (i.e. if name is not valid it gets unselected
    // find a workaround.
    //
    //Idea: simply ignoring invalid 'selected names' should work. Then we can just 
    // retain the selected names across switches. When guide is selected again
    // the selected names from before will then be remembered.

    static final ValidationResult isDownloadingMessage(GettingStartedGuide g) {
        return ValidationResult.info(g.getName() + " is downloading...");
    }

    public class CodeSetValidator extends LiveExpression<ValidationResult> {

        private LiveVariable<GettingStartedGuide> codesetProvider;
        private LiveSet<String> selectedNames;
        private LiveExpression<String[]> validCodesetNames;

        public CodeSetValidator(LiveVariable<GettingStartedGuide> guide, LiveSet<String> codesets,
                LiveExpression<String[]> validCodeSetNames) {
            this.codesetProvider = guide;
            this.selectedNames = codesets;
            this.validCodesetNames = validCodeSetNames;
            this.dependsOn(guide);
            this.dependsOn(codesets);
            this.dependsOn(validCodeSetNames);
        }

        @Override
        protected ValidationResult compute() {
            try {
                GettingStartedGuide g = codesetProvider.getValue();
                if (g != null) { //Don't check or produce errors unless a content provider has been selected.
                    boolean codesetSelected = false;
                    try {
                        Set<String> names = selectedNames.getValue();
                        if (names != null && !names.isEmpty()) {
                            for (String name : names) {
                                CodeSet cs = g.getCodeSet(name);
                                if (cs != null) {
                                    codesetSelected = true;
                                    ImportConfiguration conf = ImportUtils.importConfig(g, cs);
                                    ValidationResult valid = ImportUtils.validateImportConfiguration(conf);
                                    if (!valid.isOk()) {
                                        return valid;
                                    }
                                }
                            }
                        }
                        if (!codesetSelected) {
                            //Selectiong nothing is only allowed if there is in fact nothing to select
                            //otherwise at least on codeset must be selected for import.
                            String[] validNames = validCodesetNames.getValue();
                            if (validNames != null && validNames.length > 0) {
                                return ValidationResult.error("At least one codeset should be selected");
                            }
                        }
                    } catch (UIThreadDownloadDisallowed e) {
                        scheduleDownloadJob();
                        return isDownloadingMessage(g);
                    }
                }
            } catch (Throwable e) {
                //Unexpected. So log it for more info but also try to create a sensible error message in
                // the wizard.
                GettingStartedActivator.log(e);
                return ValidationResult.error(ExceptionUtil.getMessage(e));
            }
            return ValidationResult.OK;
        }
    }

    /**
     * The chosen guide to import stuff from.
     */
    private LiveVariable<GettingStartedGuide> guide = new LiveVariable<GettingStartedGuide>();

    /**
     * The names of the codesets selected for import.
     */
    private LiveSet<String> codesets = new LiveSet<String>(new HashSet<String>());
    {
        codesets.addAll(GettingStartedGuide.defaultCodesetNames); //Select both codesets by default.
    }

    /**
     * The valid codeset names w.r.t. the currently selected guide
     */
    public final LiveExpression<String[]> validCodesetNames = new LiveExpression<String[]>(null) {

        @Override
        protected String[] compute() {
            try {
                GettingStartedGuide g = guide.getValue();
                if (g != null) {
                    List<CodeSet> validSets = g.getCodeSets();
                    if (validSets != null) {
                        String[] names = new String[validSets.size()];
                        for (int i = 0; i < names.length; i++) {
                            names[i] = validSets.get(i).getName();
                        }
                        return names;
                    }
                }
            } catch (UIThreadDownloadDisallowed e) {
                //Failed because content is not yet downloaded but this is ok... 
                //just schedule download to happen later and in the mean time return something sensible
                scheduleDownloadJob();
            } catch (Throwable e) {
                GettingStartedActivator.log(e);
            }
            return GettingStartedGuide.defaultCodesetNames;
        }
    };

    /**
     * The build type chosen by user
     */
    private LiveVariable<BuildType> buildType = new LiveVariable<BuildType>(BuildType.DEFAULT);

    private LiveExpression<ValidationResult> guideValidator = Validator.notNull(guide, "A Guide must be selected");
    private LiveExpression<ValidationResult> codesetValidator = new CodeSetValidator(guide, codesets,
            validCodesetNames);
    private LiveExpression<ValidationResult> buildTypeValidator = new Validator() {
        @Override
        protected ValidationResult compute() {
            try {
                GettingStartedGuide g = guide.getValue();
                if (g != null) {
                    try {
                        BuildType bt = buildType.getValue();
                        if (bt == null) {
                            return ValidationResult.error("No build type selected");
                        } else {
                            List<String> codesetNames = codesets.getValues();
                            if (codesetNames != null) {
                                for (String csname : codesetNames) {
                                    CodeSet cs = g.getCodeSet(csname);
                                    if (cs != null) {
                                        ValidationResult result = cs.validateBuildType(bt);
                                        if (!result.isOk()) {
                                            return result.withMessage("CodeSet '" + csname + "': " + result.msg);
                                        }
                                        ImportStrategy importStrategy = bt.getImportStrategy();
                                        if (!importStrategy.isSupported()) {
                                            //This means some required STS component like m2e or gradle tooling is not installed
                                            return ValidationResult.error(bt.getNotInstalledMessage());
                                        }
                                    }
                                }
                            }
                        }
                    } catch (UIThreadDownloadDisallowed e) {
                        //Careful... check some of the validation will trigger downloads. This is not allowed in UI thread.
                        scheduleDownloadJob();
                        return isDownloadingMessage(g);
                    }
                }
                return ValidationResult.OK;
            } catch (Throwable e) {
                GettingStartedActivator.log(e);
                return ValidationResult.error(ExceptionUtil.getMessage(e));
            }
        }

    };

    public LiveExpression<Boolean> isDownloaded = new LiveExpression<Boolean>(false) {
        @Override
        protected Boolean compute() {
            GettingStartedGuide g = guide.getValue();
            return g == null || g.isDownloaded();
        }
    };

    /**
     * The description of the current guide.
     */
    public final LiveExpression<String> description = new LiveExpression<String>("<no description>") {
        @Override
        protected String compute() {
            GettingStartedGuide g = guide.getValue();
            if (g != null) {
                return g.getDescription();
            }
            return "<no guide selected>";
        }
    };

    public final LiveExpression<URL> homePage = new LiveExpression<URL>(null) {
        @Override
        protected URL compute() {
            GettingStartedGuide g = guide.getValue();
            if (g != null) {
                return g.getHomePage();
            }
            return null;
        }
    };

    /**
     * Indicates whether the user has selected the option to open the home page.
     */
    private LiveVariable<Boolean> enableOpenHomePage = new LiveVariable<Boolean>(true);

    {
        buildTypeValidator.dependsOn(guide);
        buildTypeValidator.dependsOn(isDownloaded);
        buildTypeValidator.dependsOn(buildType);
        buildTypeValidator.dependsOn(codesets);

        isDownloaded.dependsOn(guide);

        description.dependsOn(guide);

        homePage.dependsOn(guide);

        validCodesetNames.dependsOn(guide);
        validCodesetNames.dependsOn(isDownloaded);

        codesetValidator.dependsOn(isDownloaded);
        //Note: some other dependsOn are registered inside CodeSetValidator class itself. 
        // isDownloaded is an exception because is still null when CodeSetValidator class gets
        // instantiated.
    }

    /**
     * Downloads currently selected guide content (if it is not already cached locally.
     */
    public void performDownload(IProgressMonitor mon) throws Exception {
        mon.beginTask("Downloading", 1);
        try {
            GettingStartedGuide g = guide.getValue();
            if (g != null) {
                g.getZip().getFile(); //This forces download
            }
        } finally {
            isDownloaded.refresh();
            mon.done();
        }
    }

    private void scheduleDownloadJob() {
        Job job = new Job("Downloading guide content") {
            protected IStatus run(IProgressMonitor mon) {
                try {
                    performDownload(mon);
                } catch (Throwable e) {
                    return ExceptionUtil.status(e);
                }
                return Status.OK_STATUS;
            }

        };
        job.schedule();
    }

    /**
     * Performs the final step of the wizard when user clicks on Finish button.
     * @throws InterruptedException 
     * @throws InvocationTargetException 
     */
    public boolean performFinish(IProgressMonitor mon) throws InvocationTargetException, InterruptedException {
        //The import will be carried out with whatever the currently selected values are
        // in all the input fields / variables / widgets.
        GettingStartedGuide g = guide.getValue();
        BuildType bt = buildType.getValue();
        Set<String> codesetNames = codesets.getValue();

        mon.beginTask("Import guide content", codesetNames.size() + 1);
        try {
            for (String name : codesetNames) {
                CodeSet cs = g.getCodeSet(name);
                if (cs == null) {
                    //Ignore 'invalid' codesets. This is a bit of a hack so that we can retain selected codeset names
                    //  across guide selection changes. To do that we remember 'selected' cs names even if they
                    //  aren't valid for the current guide. That way the checkbox state stays consistent
                    //  when switching between guides (otherwise 'invalid' names would have to be cleared when switching to
                    //  a guide). 
                    mon.worked(1);
                } else {
                    IRunnableWithProgress oper = bt.getImportStrategy()
                            .createOperation(ImportUtils.importConfig(g, cs));
                    oper.run(new SubProgressMonitor(mon, 1));
                }
            }
            if (enableOpenHomePage.getValue()) {
                URL url = homePage.getValue();
                if (url != null) {
                    WebDashboardPage.openUrl(url.toString());
                }
            }
            return true;
        } catch (UIThreadDownloadDisallowed e) {
            //This shouldn't be possible... Finish button won't be enabled unless all is validated. 
            //This implies the content has been downloaded (can't be validated otherwise).
            GettingStartedActivator.log(e);
            return false;
        } finally {
            mon.done();
        }
    }

    public void setGuide(GettingStartedGuide guide) {
        this.guide.setValue(guide);
    }

    public GettingStartedGuide getGuide() {
        return guide.getValue();
    }

    public SelectionModel<BuildType> getBuildTypeModel() {
        return new SelectionModel<BuildType>(buildType, buildTypeValidator);
    }

    public SelectionModel<GettingStartedGuide> getGuideSelectionModel() {
        return new SelectionModel<GettingStartedGuide>(guide, guideValidator);
    }

    public MultiSelectionModel<String> getCodeSetModel() {
        return new MultiSelectionModel<String>(codesets, codesetValidator);
    }

    public LiveExpression<Boolean> isDownloaded() {
        return isDownloaded;
    }

    public LiveVariable<Boolean> getEnableOpenHomePage() {
        return enableOpenHomePage;
    }

}