content: /**
   *  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();
  
  }
  