/******************************************************************************* * 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.wizard.template; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.ui.PlatformUI; import org.springframework.ide.eclipse.wizard.WizardPlugin; import org.springframework.ide.eclipse.wizard.template.infrastructure.SimpleProjectFactory; import org.springframework.ide.eclipse.wizard.template.infrastructure.Template; import org.springframework.ide.eclipse.wizard.template.infrastructure.ui.WizardUIInfo; import org.springframework.ide.eclipse.wizard.template.infrastructure.ui.WizardUIInfoElement; /** * Section responsible for creating a Spring project from a template. Note that * the actual template contents are not downloaded until a user clicks the * "Next" button in the wizard, which in turn requests this section for * additional pages. However, in some simple template cases that do not * contribute additional wizard pages, the template contents are downloaded only * when a user clicks "Finish" and a project is about to be created. The "Next" * button is enabled/disabled by a lighter weight calculation that does not * require to download the contents of the template to determine if additional * pages are present or not. * */ public class TemplateWizardSection extends SpringProjectWizardSection { protected ITemplateWizardPage firstTemplatePage = null; public TemplateWizardSection(NewSpringProjectWizard wizard) { super(wizard); } @Override public boolean canProvide(ProjectWizardDescriptor descriptor) { return descriptor != null && descriptor.getTemplate() != null && !SimpleProjectFactory.SIMPLE_JAVA_TEMPLATE_ID.equals(descriptor.getTemplate().getItem().getId()); } public List<TemplateInputCollector> getTemplateInputHandlers() { List<TemplateInputCollector> handlers = new ArrayList<TemplateInputCollector>(); IWizardPage page = firstTemplatePage; while (page != null) { if (page instanceof NewTemplateWizardPage) { TemplateInputCollector handler = ((NewTemplateWizardPage) page).getInputHandler(); if (handler != null) { handlers.add(handler); } } page = page.getNextPage(); } return handlers; } @Override public IWizardPage getNextPage(IWizardPage page) { // Templates are downloaded only when a user clicks "Next" if (page == getWizard().getMainPage()) { final Template template = getWizard().getModel().selectedTemplate.getValue(); // First, clear the cached first template page, as to not show the // wrong page firstTemplatePage = null; if (template == null) { // no need to log an error that a template was not // selected, template validation occurs in the template // selection part // itself. return null; } else if (template instanceof SimpleProject && !((SimpleProject) template).hasWizardPages()) { // Handle the special case simple projects with no pages return null; } else { // Download the template contents for templates that contribute // additional UI pages, as the // content will determine the UI controls of the additional // pages. if (!TemplateUtils.hasBeenDownloaded(template)) { final IStatus[] errors = new IStatus[1]; PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { public void run() { try { getWizard().getContainer().run(true, true, new TemplateDataUIJob(template, getWizard().getShell())); } catch (InvocationTargetException ce) { String errorMessage = ErrorUtils.getErrorMessage(ce); errors[0] = new Status(IStatus.ERROR, WizardPlugin.PLUGIN_ID, errorMessage, ce); } catch (InterruptedException ie) { errors[0] = new Status(IStatus.ERROR, WizardPlugin.PLUGIN_ID, "Interrupt exception while downloading data for " + template.getName()); } } }); if (errors[0] != null && !errors[0].isOK()) { handleError(errors[0]); return null; } // Let the UI know that the template was downloaded. This is // Only needed as long as the content manager does not fire // a more accurate event indicating which template changed. // Once the content manager is updated with that change, // this // call back is no longer needed getWizard().getMainPage().refreshTemplateInUI(); } WizardUIInfo info = getUIInfo(template); if (info == null || !hasTemplateWizardPages()) { // Update the buttons so that the "Next" button is disabled. // This is done // indirectly by the wizard which calls hasNext() once // again to determine the button states based on data // already downloaded. getWizard().getContainer().updateButtons(); return null; } ITemplateWizardPage previousPage = null; ITemplateWizardPage templatePage = null; ITemplateWizardPage firstPage = null; try { for (int i = 0; i < info.getPageCount(); i++) { List<WizardUIInfoElement> pageElements = info.getElementsForPage(i); if (pageElements == null || pageElements.isEmpty()) { continue; } templatePage = new NewTemplateWizardPage(info.getPage(i).getDescription(), template, new TemplateInputCollector(pageElements)); templatePage.setWizard(getWizard()); // Always set a new first template page, as the template // selection may have changed and may // have a different associated template page if (firstPage == null) { firstPage = templatePage; } if (previousPage != null) { previousPage.setNextPage(templatePage); } previousPage = templatePage; } } catch (Exception e) { String error = ErrorUtils.getErrorMessage("Failed to load wizard page for project template for " + template.getName(), e); handleError(new Status(Status.ERROR, WizardPlugin.PLUGIN_ID, error, e)); } // Regardless of whether wizard pages where successfully // resolved for the given template, update // the first page value, even if it is null so that the wizard // does not display a previous first page of another // template for the current template. firstTemplatePage = firstPage; return firstTemplatePage; } } return null; } @Override public boolean canFinish() { boolean canFinish = super.canFinish(); if (canFinish) { Template template = getWizard().getModel().selectedTemplate.getValue(); // For now, any simple template project can complete from the first // wizard page. if (!(template instanceof SimpleProject)) { // Non-simple project templates should already be downloaded by // now, and therefore // either have a template wizard page that can be checked for // completeness or indicate it did not provide pages. if (TemplateUtils.hasBeenDownloaded(template)) { WizardUIInfo info = getUIInfo(template); if (info == null) { canFinish = false; } else { canFinish = info.getPageCount() == 0 || info.getElementsForPage(0) == null || info.getElementsForPage(0).isEmpty() || (firstTemplatePage != null && firstTemplatePage.isPageComplete()); } } else { canFinish = false; } } } return canFinish; } @Override public boolean hasNextPage(IWizardPage currentPage) { // This check is performed to enable/disable the Next button without // having to download the contents of the template // Only check if there is one more page after the main page. Any // subsequent pages after the second page added by the second page. if (currentPage == getWizard().getMainPage()) { Template template = getWizard().getModel().selectedTemplate.getValue(); if (template == null) { return false; } else if (template instanceof SimpleProject) { return ((SimpleProject) template).hasWizardPages(); } else { // Otherwise, determine if the template has been downloaded and // information can be // determined from the wizard ui info. return hasTemplateWizardPages(); } } return false; } /** * Determines if a non-Simple Project template contributes wizard pages * based entire on the downloaded wizard UI info. If no data has been * downloaded yet, and a template is selected, it will assume it does * contribute pages. This does not handle Simple Projects, which should not * be contributing wizard pages through the wizard UI info. */ protected boolean hasTemplateWizardPages() { Template template = getWizard().getModel().selectedTemplate.getValue(); if (template == null || template instanceof SimpleProject) { return false; } // If it hasn't been download, there is no way to know if the template // contributes pages or not, so // by default assume it does. Later, after its been downloaded, if it // turns out that the template did // not contribute any pages, the wizard buttons will be updated // accordingly. if (TemplateUtils.hasBeenDownloaded(template)) { WizardUIInfo info = getUIInfo(template); return info != null && info.getPageCount() > 0 && info.getElementsForPage(0) != null && !info.getElementsForPage(0).isEmpty(); } return true; } @Override public ProjectConfiguration getProjectConfiguration() throws CoreException { Template template = getWizard().getModel().selectedTemplate.getValue(); final WizardUIInfo uiInfo = getUIInfo(template); if (uiInfo == null) { throw new CoreException(new Status(IStatus.ERROR, WizardPlugin.PLUGIN_ID, "Failed to read template wizard UI info file for template: " + template.getName() + ". Check the template installation location and verify that the files are accessible.")); } String projectName = getWizard().getModel().projectName.getValue(); String uriValue = getWizard().getModel().projectLocation.getValue(); URI uri = uriValue != null ? new File(uriValue).toURI() : null; TemplateProjectConfigurationDescriptor descriptor = new TemplateProjectConfigurationDescriptor( uiInfo.getProjectNameToken(), projectName, uiInfo.getTopLevelPackageTokens(), template, uri, getTemplateInputHandlers(), getWizard().getMainPage().getVersion()); return new TemplateProjectConfiguration(descriptor, getWizard().getShell()); } }