content: /*******************************************************************************
   * Copyright (c) 2000, 2015 IBM Corporation and others.
   * 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:
   *     IBM Corporation - initial API and implementation
   *     Remy Chi Jian Suen <remy.suen@gmail.com>
   *     		- Bug 44162 [Wizards]  Define constants for wizard ids of new.file, new.folder, and new.project
   *     Angelo Zerr <angelo.zerr@gmail.com> - adapt for TypeScript init project.
   *******************************************************************************/
  package ts.eclipse.ide.ui.wizards;
  
  import java.io.File;
  import java.lang.reflect.InvocationTargetException;
  import java.net.URI;
  import java.nio.charset.StandardCharsets;
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.StringTokenizer;
  
  import org.eclipse.core.commands.ExecutionException;
  import org.eclipse.core.filesystem.URIUtil;
  import org.eclipse.core.resources.IProject;
  import org.eclipse.core.resources.IProjectDescription;
  import org.eclipse.core.resources.IResourceStatus;
  import org.eclipse.core.resources.IWorkspace;
  import org.eclipse.core.resources.ResourcesPlugin;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IConfigurationElement;
  import org.eclipse.core.runtime.IExecutableExtension;
  import org.eclipse.core.runtime.IPath;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.core.runtime.Status;
  import org.eclipse.jface.dialogs.ErrorDialog;
  import org.eclipse.jface.dialogs.IDialogConstants;
  import org.eclipse.jface.dialogs.IDialogSettings;
  import org.eclipse.jface.dialogs.MessageDialogWithToggle;
  import org.eclipse.jface.operation.IRunnableWithProgress;
  import org.eclipse.jface.preference.IPreferenceStore;
  import org.eclipse.jface.resource.ImageDescriptor;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.osgi.util.NLS;
  import org.eclipse.tm.terminal.view.core.interfaces.constants.ITerminalsConnectorConstants;
  import org.eclipse.ui.IPerspectiveDescriptor;
  import org.eclipse.ui.IPerspectiveRegistry;
  import org.eclipse.ui.IPluginContribution;
  import org.eclipse.ui.IWorkbench;
  import org.eclipse.ui.IWorkbenchPage;
  import org.eclipse.ui.IWorkbenchPreferenceConstants;
  import org.eclipse.ui.IWorkbenchWindow;
  import org.eclipse.ui.IWorkingSet;
  import org.eclipse.ui.PlatformUI;
  import org.eclipse.ui.WorkbenchException;
  import org.eclipse.ui.activities.IActivityManager;
  import org.eclipse.ui.activities.IIdentifier;
  import org.eclipse.ui.activities.IWorkbenchActivitySupport;
  import org.eclipse.ui.activities.WorkbenchActivityHelper;
  import org.eclipse.ui.dialogs.WizardNewProjectReferencePage;
  import org.eclipse.ui.ide.IDE;
  import org.eclipse.ui.internal.IPreferenceConstants;
  import org.eclipse.ui.internal.WorkbenchPlugin;
  import org.eclipse.ui.internal.ide.IDEInternalPreferences;
  import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
  import org.eclipse.ui.internal.ide.StatusUtil;
  import org.eclipse.ui.internal.registry.PerspectiveDescriptor;
  import org.eclipse.ui.internal.util.PrefUtil;
  import org.eclipse.ui.internal.wizards.newresource.ResourceMessages;
  import org.eclipse.ui.statushandlers.StatusAdapter;
  import org.eclipse.ui.statushandlers.StatusManager;
  import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard;
  
  import ts.eclipse.ide.core.utils.TypeScriptResourceUtil;
  import ts.eclipse.ide.terminal.interpreter.CommandTerminalService;
  import ts.eclipse.ide.terminal.interpreter.LineCommand;
  import ts.eclipse.ide.ui.TypeScriptUIPlugin;
  import ts.utils.FileUtils;
  
  /**
   * Standard workbench wizard that creates a new TypeScript project resource in
   * the workspace.
   * <p>
   * This class may be instantiated and used without further configuration; this
   * class is not intended to be subclassed.
   * </p>
   * <p>
   * Example:
   *
   * <pre>
   * IWorkbenchWizard wizard = new NewTypeScriptProjectWizard();
   * wizard.init(workbench, selection);
   * WizardDialog dialog = new WizardDialog(shell, wizard);
   * dialog.open();
   * </pre>
   *
   * During the call to <code>open</code>, the wizard dialog is presented to the
   * user. When the user hits Finish, a project resource with the user-specified
   * name is created, the dialog closes, and the call to <code>open</code>
   * returns.
   * </p>
   *
   * @noextend This class is not intended to be subclassed by clients.
   */
  public abstract class AbstractNewProjectWizard extends BasicNewResourceWizard implements IExecutableExtension {
  
  	/**
  	 * The wizard id for creating new projects in the workspace.
  	 *
  	 * @since 3.4
  	 */
  	// public static final String WIZARD_ID =
  	// "org.eclipse.ui.wizards.new.project"; //$NON-NLS-1$
  
  	protected AbstractWizardNewTypeScriptProjectCreationPage mainPage;
  
  	private WizardNewProjectReferencePage referencePage;
  
  	// cache of newly-created project
  	private IProject newProject;
  
  	/**
  	 * The config element which declares this wizard.
  	 */
  	private IConfigurationElement configElement;
  
  	private static String WINDOW_PROBLEMS_TITLE = ResourceMessages.NewProject_errorOpeningWindow;
  
  	/**
  	 * Extension attribute name for final perspective.
  	 */
  	private static final String FINAL_PERSPECTIVE = "finalPerspective"; //$NON-NLS-1$
  
  	/**
  	 * Extension attribute name for preferred perspectives.
  	 */
  	private static final String PREFERRED_PERSPECTIVES = "preferredPerspectives"; //$NON-NLS-1$
  
  	private final String newProjectTitle;
  
  	private final String newProjectDescription;
  
  	/**
  	 * Creates a wizard for creating a new project resource in the workspace.
  	 */
  	public AbstractNewProjectWizard(String sectionName, String newProjectTitle, String newProjectDescription) {
  		this.newProjectTitle = newProjectTitle;
  		this.newProjectDescription = newProjectDescription;
  		IDialogSettings workbenchSettings = TypeScriptUIPlugin.getDefault().getDialogSettings();
  		IDialogSettings section = workbenchSettings.getSection(sectionName);// $NON-NLS-1$
  		if (section == null) {
  			section = workbenchSettings.addNewSection(sectionName);// $NON-NLS-1$
  		}
  		setDialogSettings(section);
  	}
  
  	@Override
  	public void addPages() {
  		super.addPages();
  
  		mainPage = createMainPage();
  		mainPage.setTitle(newProjectTitle);
  		mainPage.setDescription(newProjectDescription);
  		this.addPage(mainPage);
  
  		// only add page if there are already projects in the workspace
  		/*
  		 * if (ResourcesPlugin.getWorkspace().getRoot().getProjects().length >
  		 * 0) { referencePage = new
  		 * WizardNewProjectReferencePage("basicReferenceProjectPage");//$NON-NLS
  		 * -1$
  		 * referencePage.setTitle(ResourceMessages.NewProject_referenceTitle);
  		 * referencePage.setDescription(ResourceMessages.
  		 * NewProject_referenceDescription); this.addPage(referencePage); }
  		 */
  
  	}
  
  	protected AbstractWizardNewTypeScriptProjectCreationPage createMainPage() {
  		return new WizardNewTypeScriptProjectCreationPage("basicNewProjectPage", this);
  	}
  
  	/**
  	 * Creates a new project resource with the selected name.
  	 * <p>
  	 * In normal usage, this method is invoked after the user has pressed Finish
  	 * on the wizard; the enablement of the Finish button implies that all
  	 * controls on the pages currently contain valid values.
  	 * </p>
  	 * <p>
  	 * Note that this wizard caches the new project once it has been
  	 * successfully created; subsequent invocations of this method will answer
  	 * the same project resource without attempting to create it again.
  	 * </p>
  	 *
  	 * @return the created project resource, or <code>null</code> if the project
  	 *         was not created
  	 */
  	private IProject createNewProject() {
  		if (newProject != null) {
  			return newProject;
  		}
  
  		// get a project handle
  		final IProject newProjectHandle = mainPage.getProjectHandle();
  
  		// get a project descriptor
  		URI location = null;
  		if (!mainPage.useDefaults()) {
  			location = mainPage.getLocationURI();
  		}
  
  		IWorkspace workspace = ResourcesPlugin.getWorkspace();
  		final IProjectDescription description = workspace.newProjectDescription(newProjectHandle.getName());
  		description.setLocationURI(location);
  
  		// update the referenced project if provided
  		if (referencePage != null) {
  			IProject[] refProjects = referencePage.getReferencedProjects();
  			if (refProjects.length > 0) {
  				description.setReferencedProjects(refProjects);
  			}
  		}
  
  		// create the new project operation
  		IPath projectLocationPath = URIUtil.toPath(mainPage.getLocationURI());
  		IRunnableWithProgress op = getRunnable(newProjectHandle, description, projectLocationPath);
  
  		// run the new project creation operation
  		try {
  			getContainer().run(true, true, op);
  		} catch (InterruptedException e) {
  			return null;
  		} catch (InvocationTargetException e) {
  			Throwable t = e.getTargetException();
  			if (t instanceof ExecutionException && t.getCause() instanceof CoreException) {
  				CoreException cause = (CoreException) t.getCause();
  				StatusAdapter status;
  				if (cause.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
  					status = new StatusAdapter(StatusUtil.newStatus(IStatus.WARNING,
  							NLS.bind(ResourceMessages.NewProject_caseVariantExistsError, newProjectHandle.getName()),
  							cause));
  				} else {
  					status = new StatusAdapter(StatusUtil.newStatus(cause.getStatus().getSeverity(),
  							ResourceMessages.NewProject_errorMessage, cause));
  				}
  				status.setProperty(StatusAdapter.TITLE_PROPERTY, ResourceMessages.NewProject_errorMessage);
  				StatusManager.getManager().handle(status, StatusManager.BLOCK);
  			} else {
  				StatusAdapter status = new StatusAdapter(new Status(IStatus.WARNING, IDEWorkbenchPlugin.IDE_WORKBENCH,
  						0, NLS.bind(ResourceMessages.NewProject_internalError, t.getMessage()), t));
  				status.setProperty(StatusAdapter.TITLE_PROPERTY, ResourceMessages.NewProject_errorMessage);
  				StatusManager.getManager().handle(status, StatusManager.LOG | StatusManager.BLOCK);
  			}
  			return null;
  		}
  
  		newProject = newProjectHandle;
  
  		return newProject;
  	}
  
  	/**
  	 * Returns the newly created project.
  	 *
  	 * @return the created project, or <code>null</code> if project not created
  	 */
  	public IProject getNewProject() {
  		return newProject;
  	}
  
  	@Override
  	public void init(IWorkbench workbench, IStructuredSelection currentSelection) {
  		super.init(workbench, currentSelection);
  		setNeedsProgressMonitor(true);
  		setWindowTitle(ResourceMessages.NewProject_windowTitle);
  	}
  
  	@Override
  	protected void initializeDefaultPageImageDescriptor() {
  		ImageDescriptor desc = IDEWorkbenchPlugin.getIDEImageDescriptor("wizban/newprj_wiz.png");//$NON-NLS-1$
  		setDefaultPageImageDescriptor(desc);
  	}
  
  	private static void openInNewWindow(IPerspectiveDescriptor desc) {
  
  		// Open the page.
  		try {
  			PlatformUI.getWorkbench().openWorkbenchWindow(desc.getId(), ResourcesPlugin.getWorkspace().getRoot());
  		} catch (WorkbenchException e) {
  			IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
  			if (window != null) {
  				ErrorDialog.openError(window.getShell(), WINDOW_PROBLEMS_TITLE, e.getMessage(), e.getStatus());
  			}
  		}
  	}
  
  	@Override
  	public boolean performFinish() {
  		createNewProject();
  
  		if (newProject == null) {
  			return false;
  		}
  
  		IWorkingSet[] workingSets = mainPage.getSelectedWorkingSets();
  		getWorkbench().getWorkingSetManager().addToWorkingSets(newProject, workingSets);
  
  		updatePerspective();
  		selectAndReveal(newProject);
  
  		return true;
  	}
  
  	/** Executes the given Commands in a Terminal. */
  	protected void executeCommandsInTerminal(String terminalId, List<LineCommand> commands, Map<String, Object> properties) {
  		// Prepare terminal properties
  		properties.put(ITerminalsConnectorConstants.PROP_TITLE, terminalId);
  		properties.put(ITerminalsConnectorConstants.PROP_ENCODING, StandardCharsets.UTF_8.name());
  		properties.put(ITerminalsConnectorConstants.PROP_DELEGATE_ID,
  				"ts.eclipse.ide.terminal.interpreter.LocalInterpreterLauncherDelegate");
  		properties.put(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID,
  				"org.eclipse.tm.terminal.connector.local.LocalConnector");
  		CommandTerminalService.getInstance().executeCommand(commands, terminalId, properties, null);
  	}
  
  	private static void replaceCurrentPerspective(IPerspectiveDescriptor persp) {
  
  		// Get the active page.
  		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
  		if (window == null) {
  			return;
  		}
  		IWorkbenchPage page = window.getActivePage();
  		if (page == null) {
  			return;
  		}
  
  		// Set the perspective.
  		page.setPerspective(persp);
  	}
  
  	/**
  	 * Stores the configuration element for the wizard. The config element will
  	 * be used in <code>performFinish</code> to set the result perspective.
  	 */
  	@Override
  	public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
  		configElement = cfig;
  	}
  
  	/**
  	 * Updates the perspective for the active page within the window.
  	 */
  	protected void updatePerspective() {
  		updatePerspective(configElement);
  	}
  
  	/**
  	 * Updates the perspective based on the current settings in the
  	 * Workbench/Perspectives preference page.
  	 *
  	 * Use the setting for the new perspective opening if we are set to open in
  	 * a new perspective.
  	 * <p>
  	 * A new project wizard class will need to implement the
  	 * <code>IExecutableExtension</code> interface so as to gain access to the
  	 * wizard's <code>IConfigurationElement</code>. That is the configuration
  	 * element to pass into this method.
  	 * </p>
  	 *
  	 * @param configElement
  	 *            - the element we are updating with
  	 *
  	 * @see IPreferenceConstants#OPM_NEW_WINDOW
  	 * @see IPreferenceConstants#OPM_ACTIVE_PAGE
  	 * @see IWorkbenchPreferenceConstants#NO_NEW_PERSPECTIVE
  	 */
  	public static void updatePerspective(IConfigurationElement configElement) {
  		// Do not change perspective if the configuration element is
  		// not specified.
  		if (configElement == null) {
  			return;
  		}
  
  		// Retrieve the new project open perspective preference setting
  		String perspSetting = PrefUtil.getAPIPreferenceStore().getString(IDE.Preferences.PROJECT_OPEN_NEW_PERSPECTIVE);
  
  		String promptSetting = IDEWorkbenchPlugin.getDefault().getPreferenceStore()
  				.getString(IDEInternalPreferences.PROJECT_SWITCH_PERSP_MODE);
  
  		// Return if do not switch perspective setting and are not prompting
  		if (!(promptSetting.equals(MessageDialogWithToggle.PROMPT))
  				&& perspSetting.equals(IWorkbenchPreferenceConstants.NO_NEW_PERSPECTIVE)) {
  			return;
  		}
  
  		// Read the requested perspective id to be opened.
  		String finalPerspId = configElement.getAttribute(FINAL_PERSPECTIVE);
  		if (finalPerspId == null) {
  			return;
  		}
  
  		// Map perspective id to descriptor.
  		IPerspectiveRegistry reg = PlatformUI.getWorkbench().getPerspectiveRegistry();
  
  		// leave this code in - the perspective of a given project may map to
  		// activities other than those that the wizard itself maps to.
  		IPerspectiveDescriptor finalPersp = reg.findPerspectiveWithId(finalPerspId);
  		if (finalPersp != null && finalPersp instanceof IPluginContribution) {
  			IPluginContribution contribution = (IPluginContribution) finalPersp;
  			if (contribution.getPluginId() != null) {
  				IWorkbenchActivitySupport workbenchActivitySupport = PlatformUI.getWorkbench().getActivitySupport();
  				IActivityManager activityManager = workbenchActivitySupport.getActivityManager();
  				IIdentifier identifier = activityManager
  						.getIdentifier(WorkbenchActivityHelper.createUnifiedId(contribution));
  				Set idActivities = identifier.getActivityIds();
  
  				if (!idActivities.isEmpty()) {
  					Set enabledIds = new HashSet(activityManager.getEnabledActivityIds());
  
  					if (enabledIds.addAll(idActivities)) {
  						workbenchActivitySupport.setEnabledActivityIds(enabledIds);
  					}
  				}
  			}
  		} else {
  			IDEWorkbenchPlugin.log("Unable to find persective " //$NON-NLS-1$
  					+ finalPerspId + " in BasicNewProjectResourceWizard.updatePerspective"); //$NON-NLS-1$
  			return;
  		}
  
  		// gather the preferred perspectives
  		// always consider the final perspective (and those derived from it)
  		// to be preferred
  		ArrayList preferredPerspIds = new ArrayList();
  		addPerspectiveAndDescendants(preferredPerspIds, finalPerspId);
  		String preferred = configElement.getAttribute(PREFERRED_PERSPECTIVES);
  		if (preferred != null) {
  			StringTokenizer tok = new StringTokenizer(preferred, " \t\n\r\f,"); //$NON-NLS-1$
  			while (tok.hasMoreTokens()) {
  				addPerspectiveAndDescendants(preferredPerspIds, tok.nextToken());
  			}
  		}
  
  		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
  		if (window != null) {
  			IWorkbenchPage page = window.getActivePage();
  			if (page != null) {
  				IPerspectiveDescriptor currentPersp = page.getPerspective();
  
  				// don't switch if the current perspective is a preferred
  				// perspective
  				if (currentPersp != null && preferredPerspIds.contains(currentPersp.getId())) {
  					return;
  				}
  			}
  
  			// prompt the user to switch
  			if (!confirmPerspectiveSwitch(window, finalPersp)) {
  				return;
  			}
  		}
  
  		int workbenchPerspectiveSetting = WorkbenchPlugin.getDefault().getPreferenceStore()
  				.getInt(IPreferenceConstants.OPEN_PERSP_MODE);
  
  		// open perspective in new window setting
  		if (workbenchPerspectiveSetting == IPreferenceConstants.OPM_NEW_WINDOW) {
  			openInNewWindow(finalPersp);
  			return;
  		}
  
  		// replace active perspective setting otherwise
  		replaceCurrentPerspective(finalPersp);
  	}
  
  	/**
  	 * Adds to the list all perspective IDs in the Workbench who's original ID
  	 * matches the given ID.
  	 *
  	 * @param perspectiveIds
  	 *            the list of perspective IDs to supplement.
  	 * @param id
  	 *            the id to query.
  	 * @since 3.0
  	 */
  	private static void addPerspectiveAndDescendants(List perspectiveIds, String id) {
  		IPerspectiveRegistry registry = PlatformUI.getWorkbench().getPerspectiveRegistry();
  		IPerspectiveDescriptor[] perspectives = registry.getPerspectives();
  		for (int i = 0; i < perspectives.length; i++) {
  			// @issue illegal ref to workbench internal class;
  			// consider adding getOriginalId() as API on IPerspectiveDescriptor
  			PerspectiveDescriptor descriptor = ((PerspectiveDescriptor) perspectives[i]);
  			if (descriptor.getOriginalId().equals(id)) {
  				perspectiveIds.add(descriptor.getId());
  			}
  		}
  	}
  
  	/**
  	 * Prompts the user for whether to switch perspectives.
  	 *
  	 * @param window
  	 *            The workbench window in which to switch perspectives; must not
  	 *            be <code>null</code>
  	 * @param finalPersp
  	 *            The perspective to switch to; must not be <code>null</code>.
  	 *
  	 * @return <code>true</code> if it's OK to switch, <code>false</code>
  	 *         otherwise
  	 */
  	private static boolean confirmPerspectiveSwitch(IWorkbenchWindow window, IPerspectiveDescriptor finalPersp) {
  		IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
  		String pspm = store.getString(IDEInternalPreferences.PROJECT_SWITCH_PERSP_MODE);
  		if (!IDEInternalPreferences.PSPM_PROMPT.equals(pspm)) {
  			// Return whether or not we should always switch
  			return IDEInternalPreferences.PSPM_ALWAYS.equals(pspm);
  		}
  		String desc = finalPersp.getDescription();
  		String message;
  		if (desc == null || desc.length() == 0)
  			message = NLS.bind(ResourceMessages.NewProject_perspSwitchMessage, finalPersp.getLabel());
  		else
  			message = NLS.bind(ResourceMessages.NewProject_perspSwitchMessageWithDesc,
  					new String[] { finalPersp.getLabel(), desc });
  
  		MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(window.getShell(),
  				ResourceMessages.NewProject_perspSwitchTitle, message,
  				null /* use the default message for the toggle */,
  				false /* toggle is initially unchecked */, store, IDEInternalPreferences.PROJECT_SWITCH_PERSP_MODE);
  		int result = dialog.getReturnCode();
  
  		// If we are not going to prompt anymore propogate the choice.
  		if (dialog.getToggleState()) {
  			String preferenceValue;
  			if (result == IDialogConstants.YES_ID) {
  				// Doesn't matter if it is replace or new window
  				// as we are going to use the open perspective setting
  				preferenceValue = IWorkbenchPreferenceConstants.OPEN_PERSPECTIVE_REPLACE;
  			} else {
  				preferenceValue = IWorkbenchPreferenceConstants.NO_NEW_PERSPECTIVE;
  			}
  
  			// update PROJECT_OPEN_NEW_PERSPECTIVE to correspond
  			PrefUtil.getAPIPreferenceStore().setValue(IDE.Preferences.PROJECT_OPEN_NEW_PERSPECTIVE, preferenceValue);
  		}
  		return result == IDialogConstants.YES_ID;
  	}
  
  	protected String getNodeFilePath() {
  		File nodeFile = TypeScriptResourceUtil.getWorkspaceNodejsInstallPath();
  		return nodeFile != null ? FileUtils.getPath(nodeFile.getParentFile()) : null;
  	}
  	
  	protected abstract IRunnableWithProgress getRunnable(final IProject newProjectHandle,
  			final IProjectDescription description, IPath projectLocation);
  }
  