/******************************************************************************* * Copyright (c) 2013, 2017 Pivotal, 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.boot.wizard; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.Wizard; import org.eclipse.ui.IImportWizard; import org.eclipse.ui.INewWizard; import org.eclipse.ui.IWorkbench; import org.springframework.ide.eclipse.boot.core.BootActivator; import org.springframework.ide.eclipse.boot.livexp.ui.DynamicSection; import org.springsource.ide.eclipse.commons.livexp.core.FieldModel; import org.springsource.ide.eclipse.commons.livexp.ui.ChooseOneSectionCombo; import org.springsource.ide.eclipse.commons.livexp.ui.CommentSection; import org.springsource.ide.eclipse.commons.livexp.ui.DescriptionSection; import org.springsource.ide.eclipse.commons.livexp.ui.GroupSection; import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections; import org.springsource.ide.eclipse.commons.livexp.ui.ProjectLocationSection; import org.springsource.ide.eclipse.commons.livexp.ui.StringFieldSection; import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection; import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageWithSections; import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil; import org.springsource.ide.eclipse.commons.livexp.util.Parser; import com.google.common.collect.ImmutableList; public class NewSpringBootWizard extends Wizard implements INewWizard, IImportWizard { private static final ImageDescriptor IMAGE = BootWizardImages.BOOT_WIZARD_ICON; static final String NO_CONTENT_AVAILABLE = "No content available."; private InitializrFactoryModel<NewSpringBootWizardModel> model; private IStructuredSelection selection; private WorkingSetSection workingSetSection; private WizardPageWithSections projectPage; public NewSpringBootWizard() throws Exception { setDefaultPageImageDescriptor(IMAGE); } //@Override public void init(IWorkbench workbench, IStructuredSelection selection) { try { model = NewSpringBootWizardFactoryModel.create(BootActivator.getUrlConnectionFactory(), BootActivator.getDefault().getPreferenceStore()); this.selection = selection; } catch (Exception e) { MessageDialog.openError(workbench.getActiveWorkbenchWindow().getShell(), "Error opening the wizard", ExceptionUtil.getMessage(e)+"\n\n"+ "Note that this wizard uses a webservice and needs internet access.\n"+ "A more detailed error message may be found in the Eclipse error log." ); //Ensure exception is logged. (Eclipse UI may not log it!). BootWizardActivator.log(e); throw new Error(e); } } @Override public void addPages() { super.addPages(); Assert.isLegal(model!=null, "The Spring Starter Wizard model was not initialized. Unable to open the wizard."); addPage(projectPage = new ProjectDetailsPage(model)); addPage(new MultipleViewsDependencyPage(model)); addPage(new PageThree(model)); } @Override public boolean canFinish() { return super.canFinish() && getContainer().getCurrentPage()!=projectPage; } public class ProjectDetailsSection extends GroupSection { private final NewSpringBootWizardModel model; public ProjectDetailsSection(IPageWithSections owner, NewSpringBootWizardModel model) { super(owner, null); this.model = model; addSections(createSections().toArray(new WizardPageSection[0])); } protected List<WizardPageSection> createSections() { List<WizardPageSection> sections = new ArrayList<>(); FieldModel<String> projectName = model.getProjectName(); sections.add(new StringFieldSection(owner, projectName)); sections.add(new ProjectLocationSection(owner, model.getLocation(), projectName.getVariable(), model.getLocationValidator())); WizardPageSection radios = createRadioGroupsSection(owner); if (radios!=null) { sections.add(radios); } for (FieldModel<String> f : model.stringInputs) { //caution! we already created the section for projectName because we want it at the top if (projectName!=f) { sections.add(new StringFieldSection(owner, f)); } } sections.add(workingSetSection = new WorkingSetSection(owner, selection)); return sections; } private WizardPageSection createRadioGroupsSection(IPageWithSections owner) { boolean notEmpty = false; RadioGroup bootVersion = model.getBootVersion(); //This is placed specifically somewhere else so must skip it here ArrayList<WizardPageSection> radioSections = new ArrayList<>(); for (RadioGroup radioGroup : model.getRadioGroups().getGroups()) { if (radioGroup!=bootVersion) { if (radioGroup.getRadios().length>1) { //Don't add a UI elements for something that offers no real choice radioSections.add( new ChooseOneSectionCombo<>(owner, radioGroup.getLabel(), radioGroup.getSelection(), radioGroup.getRadios()) //new ChooseOneSectionCombo<RadioInfo>(owner, radioGroup.getLabel(), radioGroup.getSelection(), radioGroup.getRadios()) ); notEmpty = true; } } } if (notEmpty) { return new GroupSection(owner, null, radioSections.toArray(new WizardPageSection[radioSections.size()])).columns(2, false); } return null; } } public class ProjectDetailsPage extends WizardPageWithSections { protected InitializrFactoryModel<NewSpringBootWizardModel> model; public ProjectDetailsPage(InitializrFactoryModel<NewSpringBootWizardModel> model) { super("page1", "New Spring Starter Project", null); this.model = model; } @Override protected List<WizardPageSection> createSections() { ChooseOneSectionCombo<String> comboSection = new ChooseOneSectionCombo<>(this, model.getServiceUrlField(), model.getUrls()) .grabHorizontal(true); comboSection.allowTextEdits(Parser.IDENTITY); // Note: have to set the project page in the dynamic section because the dynamic section composite // gets created at the start, and determines the initial size of the wizard page, regardless of its content. DynamicSection dynamicSection = new DynamicSection(this, model.getModel().apply((dynamicModel) -> { if (dynamicModel != null) { return new ProjectDetailsSection(this, dynamicModel); } return new CommentSection(this, NO_CONTENT_AVAILABLE); } )); return ImmutableList.of(comboSection, dynamicSection); } } public static class PageThree extends WizardPageWithSections { private final InitializrFactoryModel<NewSpringBootWizardModel> model; protected PageThree(InitializrFactoryModel<NewSpringBootWizardModel> model) { super("page3", "New Spring Starter Project", null); this.model = model; } protected WizardPageSection createDynamicContent(NewSpringBootWizardModel model) { List<WizardPageSection> sections = new ArrayList<>(); sections.add(new GroupSection(this, "Site Info", new StringFieldSection(this, "Base Url", model.baseUrl, model.baseUrlValidator), new DescriptionSection(this, model.downloadUrl).label("Full Url").readOnly(false) )); return new GroupSection(this, null, sections.toArray(new WizardPageSection[0])).grabVertical(true); } @Override protected List<WizardPageSection> createSections() { DynamicSection dynamicSection = new DynamicSection(this, model.getModel().apply((dynamicModel) -> { if (dynamicModel != null) { return createDynamicContent(dynamicModel); } return new CommentSection(this, NO_CONTENT_AVAILABLE); } )); return ImmutableList.of(dynamicSection); } } @Override public boolean performFinish() { if (model.getModel().getValue() == null) { return false; } model.getModel().getValue().setWorkingSets(workingSetSection.getWorkingSets()); //must be in ui thread. Don't put in job! Job job = new Job("Import Getting Started Content") { @Override protected IStatus run(IProgressMonitor mon) { try { // TODO: maybe the model factory should have a perform finish that does a save, which then calls the underlying model perform finish model.save(); model.getModel().getValue().performFinish(mon); return Status.OK_STATUS; } catch (Throwable e) { return ExceptionUtil.status(e); } } }; //WARNING: Do not set a scheduling rule here. It breaks gradle import by causing a deadlock or rule conflict. //job.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); //See: https://www.pivotaltracker.com/story/show/128781771 job.setPriority(Job.BUILD); job.setUser(true); //shows progress in default eclipse config job.schedule(); return true; } }