/** * Copyright (c) 2013-2016 Angelo ZERR. * 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: * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation */ package tern.eclipse.ide.server.nodejs.ui.debugger.launchConfigurations; import java.io.File; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.accessibility.AccessibleAdapter; import org.eclipse.swt.accessibility.AccessibleEvent; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; 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.FileDialog; 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 org.eclipse.ui.dialogs.ResourceSelectionDialog; import tern.eclipse.ide.server.nodejs.core.IDENodejsProcessHelper; import tern.eclipse.ide.server.nodejs.core.INodejsInstall; import tern.eclipse.ide.server.nodejs.core.INodejsInstallManager; import tern.eclipse.ide.server.nodejs.core.TernNodejsCorePlugin; import tern.eclipse.ide.server.nodejs.core.debugger.launchConfigurations.INodejsCliFileLaunchConfigurationConstants; import tern.eclipse.ide.server.nodejs.core.debugger.launchConfigurations.NodejsCliFileHelper; import tern.eclipse.ide.server.nodejs.internal.ui.TernNodejsUIMessages; import tern.eclipse.ide.server.nodejs.internal.ui.TernNodejsUIPlugin; import tern.eclipse.ide.server.nodejs.internal.ui.preferences.DebuggerFieldEditor; import tern.eclipse.ide.server.nodejs.internal.ui.preferences.NodeJSConfigEditor; import tern.utils.StringUtils; /** * Abstract class for tab of launch of client file (ex: protractor tab). */ public abstract class AbstractNodejsCliFileLaunchConfigurationTab extends AbstractLaunchConfigurationTab { private Text cliFileField; private Button workspaceLocationButton; private Button variablesLocationButton; protected boolean fInitializing = false; private boolean userEdited = false; protected WidgetListener fListener = new WidgetListener(); private Combo debuggerField; private String[][] debuggers; private Label nodePathTitle; private Text nodePathInfo; private Combo nodeInstallField; private String[][] nodeInstalls; private Combo nodePathField; private Button nodePathButton; /** * A listener to update for text modification and widget selection. */ protected class WidgetListener extends SelectionAdapter implements ModifyListener { @Override public void modifyText(ModifyEvent e) { updateLaunchConfiguration(); } protected void updateLaunchConfiguration() { if (!fInitializing) { setDirty(true); userEdited = true; updateLaunchConfigurationDialog(); } } @Override public void widgetSelected(SelectionEvent e) { setDirty(true); Object source = e.getSource(); if (source == workspaceLocationButton) { handleWorkspaceLocationButtonSelected(); } else if (source == debuggerField || source == nodeInstallField || source == nodePathField) { updateLaunchConfiguration(); } else if (source == nodePathButton) { handleNodePathButtonSelected(); } } } @Override public void createControl(Composite parent) { Composite mainComposite = new Composite(parent, SWT.NONE); setControl(mainComposite); mainComposite.setFont(parent.getFont()); GridLayout layout = new GridLayout(); layout.numColumns = 1; GridData gridData = new GridData(GridData.FILL_HORIZONTAL); mainComposite.setLayout(layout); mainComposite.setLayoutData(gridData); createCliFileComponent(mainComposite); createDebuggerComponent(mainComposite); createNodeInstallComponent(mainComposite); } private void createCliFileComponent(Composite parent) { Group group = new Group(parent, SWT.NONE); String cliFileLabel = getCliFileLabel(); group.setText(cliFileLabel); GridLayout layout = new GridLayout(); layout.numColumns = 1; GridData gridData = new GridData(GridData.FILL_HORIZONTAL); group.setLayout(layout); group.setLayoutData(gridData); cliFileField = new Text(group, SWT.BORDER); gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH; cliFileField.setLayoutData(gridData); cliFileField.addModifyListener(fListener); addControlAccessibleListener(cliFileField, group.getText()); Composite buttonComposite = new Composite(group, SWT.NONE); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.numColumns = 3; gridData = new GridData(GridData.HORIZONTAL_ALIGN_END); buttonComposite.setLayout(layout); buttonComposite.setLayoutData(gridData); buttonComposite.setFont(parent.getFont()); workspaceLocationButton = createPushButton(buttonComposite, TernNodejsUIMessages.Button_browse_workspace, null); workspaceLocationButton.addSelectionListener(fListener); addControlAccessibleListener(workspaceLocationButton, group.getText() + " " + workspaceLocationButton.getText()); //$NON-NLS-1$ // fileLocationButton= createPushButton(buttonComposite, // TernNodejsUIMessages.ExternalToolsMainTab_Brows_e_File_System____4, // null); // fileLocationButton.addSelectionListener(fListener); // addControlAccessibleListener(fileLocationButton, group.getText() + " // " + fileLocationButton.getText()); //$NON-NLS-1$ variablesLocationButton = createPushButton(buttonComposite, TernNodejsUIMessages.Button_variables, null); variablesLocationButton.addSelectionListener(fListener); addControlAccessibleListener(variablesLocationButton, group.getText() + " " + variablesLocationButton.getText()); //$NON-NLS-1$ } private void createDebuggerComponent(Composite parent) { Group group = new Group(parent, SWT.NONE); String debuggerLabel = TernNodejsUIMessages.TernNodejsPreferencesPage_debugger_label; group.setText(debuggerLabel); GridLayout layout = new GridLayout(); layout.numColumns = 1; GridData gridData = new GridData(GridData.FILL_HORIZONTAL); group.setLayout(layout); group.setLayoutData(gridData); debuggers = DebuggerFieldEditor.createDebuggers(); debuggerField = new Combo(group, SWT.READ_ONLY); debuggerField.setFont(group.getFont()); for (int i = 0; i < debuggers.length; i++) { debuggerField.add(debuggers[i][0], i); } debuggerField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); debuggerField.addSelectionListener(fListener); addControlAccessibleListener(debuggerField, group.getText()); Link debuggerWikiLink = DebuggerFieldEditor.newWikiLink(group, SWT.NONE); GridData gd = new GridData(GridData.FILL_HORIZONTAL); debuggerWikiLink.setLayoutData(gd); } private void createNodeInstallComponent(Composite parent) { Group group = new Group(parent, SWT.NONE); String nodeInstallLabel = TernNodejsUIMessages.TernNodejsPreferencesPage_nodeJSInstall; group.setText(nodeInstallLabel); GridLayout layout = new GridLayout(); layout.numColumns = 1; GridData gridData = new GridData(GridData.FILL_HORIZONTAL); group.setLayout(layout); nodeInstalls = NodeJSConfigEditor.createNodeInstalls(); nodeInstallField = new Combo(group, SWT.READ_ONLY); nodeInstallField.setFont(group.getFont()); for (int i = 0; i < nodeInstalls.length; i++) { nodeInstallField.add(nodeInstalls[i][0], i); } nodeInstallField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); nodeInstallField.addSelectionListener(fListener); nodeInstallField.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { int nodeInstallSelectionIndex = nodeInstallField.getSelectionIndex(); String nodeInstallId = nodeInstalls[nodeInstallSelectionIndex][1]; INodejsInstall install = TernNodejsCorePlugin.getNodejsInstallManager() .findNodejsInstall(nodeInstallId); if (install == null || install.isNative()) { nodePathField.setEnabled(true); String defaultPath = IDENodejsProcessHelper.getNodejsPath(); nodePathField.setText(defaultPath); nodePathInfo.setText(defaultPath); } else { nodePathField.setEnabled(false); nodePathInfo.setText(install.getPath().getAbsolutePath()); } } }); addControlAccessibleListener(nodeInstallField, group.getText()); Composite pathComponent = new Composite(group, SWT.NONE); pathComponent.setLayout(new GridLayout(2, false)); pathComponent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); nodePathField = new Combo(pathComponent, SWT.NONE); nodePathField.setFont(pathComponent.getFont()); nodePathField.setItems(IDENodejsProcessHelper.getDefaultNodejsPaths()); nodePathField.addSelectionListener(fListener); nodePathField.addModifyListener(fListener); nodePathField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); addControlAccessibleListener(nodePathField, group.getText()); nodePathButton = createPushButton(pathComponent, TernNodejsUIMessages.Button_browse, null); nodePathButton.addSelectionListener(fListener); addControlAccessibleListener(nodePathButton, group.getText() + " " + nodePathButton.getText()); //$NON-NLS-1$ group.setLayoutData(gridData); createNodePathInfo(group); } private void createNodePathInfo(Composite parent) { // Node path label nodePathTitle = new Label(parent, SWT.NONE); nodePathTitle.setText(TernNodejsUIMessages.TernNodejsPreferencesPage_nodeJSPath); GridData gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); nodePathTitle.setLayoutData(gridData); nodePathInfo = new Text(parent, SWT.WRAP | SWT.READ_ONLY); nodePathInfo.setText(""); //$NON-NLS-1$ gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalSpan = 2; gridData.widthHint = 200; nodePathInfo.setLayoutData(gridData); } @Override public void initializeFrom(ILaunchConfiguration configuration) { fInitializing = true; updateCliFile(configuration); updateDebugger(configuration); updateNodeInstallPath(configuration); fInitializing = false; setDirty(false); } private void updateCliFile(ILaunchConfiguration configuration) { String cliFile = ""; try { cliFile = configuration.getAttribute(getCliFileLaunchAttrId(), ""); } catch (CoreException ce) { TernNodejsUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, TernNodejsUIPlugin.PLUGIN_ID, TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_Error_reading_configuration, ce)); } cliFileField.setText(cliFile); } private void updateDebugger(ILaunchConfiguration configuration) { try { String debuggerId = configuration.getAttribute(getDebuggerLaunchAttrId(), ""); for (int i = 0; i < debuggers.length; i++) { if (debuggers[i][1].equals(debuggerId)) { debuggerField.setText(debuggers[i][0]); break; } } } catch (CoreException ce) { TernNodejsUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, TernNodejsUIPlugin.PLUGIN_ID, TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_Error_reading_configuration, ce)); } } private void updateNodeInstallPath(ILaunchConfiguration configuration) { try { String nodeInstall = configuration.getAttribute(getNodeInstallLaunchAttrId(), ""); for (int i = 0; i < nodeInstalls.length; i++) { if (nodeInstalls[i][1].equals(nodeInstall)) { nodeInstallField.setText(nodeInstalls[i][0]); break; } } String nodePath = configuration.getAttribute(getNodePathLaunchAttrId(), ""); nodePathField.setText(nodePath); INodejsInstallManager installManager = TernNodejsCorePlugin.getNodejsInstallManager(); INodejsInstall nodejsInstall = installManager.findNodejsInstall(nodeInstall); nodePathField.setEnabled(nodejsInstall != null && nodejsInstall.isNative()); } catch (CoreException ce) { TernNodejsUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, TernNodejsUIPlugin.PLUGIN_ID, TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_Error_reading_configuration, ce)); } } @Override public void performApply(ILaunchConfigurationWorkingCopy configuration) { // cli file String cliFile = cliFileField.getText().trim(); if (cliFile.length() == 0) { configuration.setAttribute(getCliFileLaunchAttrId(), (String) null); } else { configuration.setAttribute(getCliFileLaunchAttrId(), cliFile); } // debugger int debuggerSelectionIndex = debuggerField.getSelectionIndex(); if (debuggerSelectionIndex >= 0) { configuration.setAttribute(getDebuggerLaunchAttrId(), debuggers[debuggerSelectionIndex][1]); } // node install int nodeInstallSelectionIndex = nodeInstallField.getSelectionIndex(); if (nodeInstallSelectionIndex >= 0) { configuration.setAttribute(getNodeInstallLaunchAttrId(), nodeInstalls[nodeInstallSelectionIndex][1]); } // node path String nodePath = nodePathField.getText(); if (nodePath.length() == 0) { configuration.setAttribute(getNodePathLaunchAttrId(), (String) null); } else { configuration.setAttribute(getNodePathLaunchAttrId(), nodePath); } } @Override public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { // cli file IFile cliFile = getDefaultCliFile(); if (cliFile == null) { configuration.setAttribute(getCliFileLaunchAttrId(), (String) null); } else { configuration.setAttribute(getCliFileLaunchAttrId(), NodejsCliFileHelper.getWorkspaceLoc(cliFile)); } // debugger String debugger = getDefaultDebugger(); if (StringUtils.isEmpty(debugger)) { configuration.setAttribute(getDebuggerLaunchAttrId(), (String) null); } else { configuration.setAttribute(getDebuggerLaunchAttrId(), debugger); } // node install String nodeInstall = getDefaultNodeInstall(); if (StringUtils.isEmpty(nodeInstall)) { configuration.setAttribute(getNodeInstallLaunchAttrId(), (String) null); } else { configuration.setAttribute(getNodeInstallLaunchAttrId(), nodeInstall); } // node path String nodePath = getDefaultNodePath(); if (StringUtils.isEmpty(nodePath)) { configuration.setAttribute(getNodePathLaunchAttrId(), (String) null); } else { configuration.setAttribute(getNodePathLaunchAttrId(), nodePath); } } /** * Prompts the user for a workspace location within the workspace and sets * the location as a String containing the workspace_loc variable or * <code>null</code> if no location was obtained from the user. */ protected void handleWorkspaceLocationButtonSelected() { ResourceSelectionDialog dialog; dialog = new ResourceSelectionDialog(getShell(), ResourcesPlugin.getWorkspace().getRoot(), TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_Select_a_client_file); dialog.open(); Object[] results = dialog.getResult(); if (results == null || results.length < 1) { return; } IResource resource = (IResource) results[0]; cliFileField.setText(NodejsCliFileHelper.getWorkspaceLoc(resource)); } protected void handleNodePathButtonSelected() { FileDialog fileDialog = new FileDialog(getShell(), SWT.NONE); fileDialog.setFileName(nodePathField.getText()); String text = fileDialog.open(); if (text != null) { nodePathField.setText(text); } } /* * Fix for Bug 60163 Accessibility: New Builder Dialog missing object info * for textInput controls */ public void addControlAccessibleListener(Control control, String controlName) { // strip mnemonic (&) String[] strs = controlName.split("&"); //$NON-NLS-1$ StringBuffer stripped = new StringBuffer(); for (int i = 0; i < strs.length; i++) { stripped.append(strs[i]); } control.getAccessible().addAccessibleListener(new ControlAccessibleListener(stripped.toString())); } private class ControlAccessibleListener extends AccessibleAdapter { private String controlName; ControlAccessibleListener(String name) { controlName = name; } @Override public void getName(AccessibleEvent e) { e.result = controlName; } } private String getCliFileLaunchAttrId() { return INodejsCliFileLaunchConfigurationConstants.ATTR_CLI_FILE; } private String getDebuggerLaunchAttrId() { return INodejsCliFileLaunchConfigurationConstants.ATTR_DEBUGGER; } private String getNodeInstallLaunchAttrId() { return INodejsCliFileLaunchConfigurationConstants.ATTR_NODE_INSTALL; } private String getNodePathLaunchAttrId() { return INodejsCliFileLaunchConfigurationConstants.ATTR_NODE_PATH; } @Override public boolean isValid(ILaunchConfiguration launchConfig) { setErrorMessage(null); setMessage(null); return (validateCliFile() && validateDebugger() && validateNodeInstallPath()); } private boolean validateCliFile() { String cliFile = this.cliFileField.getText().trim(); if (StringUtils.isEmpty(cliFile)) { return true; } String expandedLocation = null; try { expandedLocation = resolveValue(cliFile); if (expandedLocation == null) return true; } catch (CoreException e) { setErrorMessage(e.getStatus().getMessage()); return false; } File file = new File(expandedLocation); if (!(file.exists())) { setErrorMessage( TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_client_file_does_not_exist); return false; } if (!(file.isFile())) { setErrorMessage(TernNodejsUIMessages.ExternalToolsMainTab_client_file_specified_is_not_a_file); return false; } return true; } private String resolveValue(String expression) throws CoreException { String expanded = null; try { expanded = getValue(expression); } catch (CoreException localCoreException) { validateVariables(expression); throw localCoreException; } return expanded; } private String getValue(String expression) throws CoreException { IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); return manager.performStringSubstitution(expression); } private void validateVariables(String expression) throws CoreException { IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); manager.validateStringVariables(expression); } private boolean validateDebugger() { int debuggerSelectionIndex = debuggerField.getSelectionIndex(); if (debuggerSelectionIndex <= 0) { setErrorMessage(TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_debugger_required); return false; } String debuggerId = debuggers[debuggerSelectionIndex][1]; if (StringUtils.isEmpty(debuggerId)) { setErrorMessage(TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_debugger_not_installed); return false; } return true; } private boolean validateNodeInstallPath() { int nodeInstallSelectionIndex = nodeInstallField.getSelectionIndex(); if (nodeInstallSelectionIndex <= 0) { setErrorMessage(TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_nodeInstall_required); return false; } INodejsInstallManager installManager = TernNodejsCorePlugin.getNodejsInstallManager(); String nodeInstallId = nodeInstalls[nodeInstallSelectionIndex][1]; INodejsInstall nodejsInstall = installManager.findNodejsInstall(nodeInstallId); if (nodejsInstall == null) { setErrorMessage( NLS.bind(TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_nodeInstall_not_found, nodeInstallId)); return false; } if (nodejsInstall.isNative()) { // validate node path String nodePath = nodePathField.getText(); if (StringUtils.isEmpty(nodePath)) { setErrorMessage(TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_nodePath_required); return false; } if (!new File(nodePath).exists() && !nodePath.equals("node")) { setErrorMessage(NLS.bind( TernNodejsUIMessages.AbstractNodejsCliFileLaunchConfigurationTab_nodePath_not_found, nodePath)); return false; } } return true; } protected abstract String getCliFileLabel(); protected abstract IFile getDefaultCliFile(); protected abstract String getDefaultDebugger(); protected abstract String getDefaultNodeInstall(); protected abstract String getDefaultNodePath(); }