/******************************************************************************* * Copyright (c) 2012 VMWare, 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: * VMWare, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.ui.internal.properties; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.ui.MultipleInputDialog; import org.eclipse.debug.internal.ui.SWTFactory; import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationsMessages; import org.eclipse.debug.ui.EnvironmentTab; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; 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.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.grails.ide.eclipse.core.GrailsCoreActivator; import org.grails.ide.eclipse.ui.GrailsUiImages; /** * Inspired by {@link EnvironmentTab} * @author Andrew Eisenberg * @since 2.6.1 */ public class GrailsLaunchPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { private class SystemProperty { private final String name; private String value; public SystemProperty(String name, String value) { this.name = name; this.value = value; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { boolean equal = false; if (obj instanceof SystemProperty) { SystemProperty var = (SystemProperty)obj; equal = var.getName().equals(name); } return equal; } /** * Returns this variable's name, which serves as the key in the key/value * pair this variable represents * * @return this variable's name */ public String getName() { return name; } /** * Returns this variables value. * * @return this variable's value */ public String getValue() { return value; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ public int hashCode() { return name.hashCode(); } /** * Sets this variable's value * @param value */ public void setValue(String value) { this.value = value; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { return getName(); } } /** * Content provider for the environment table */ private class SystemPropertyContentProvider implements IStructuredContentProvider { public void dispose() { } public Object[] getElements(Object inputElement) { SystemProperty[] elements; Map<String, String> m = GrailsCoreActivator.getDefault().getUserSupliedLaunchSystemProperties(); if (m != null && !m.isEmpty()) { elements = new SystemProperty[m.size()]; String[] varNames = new String[m.size()]; m.keySet().toArray(varNames); for (int i = 0; i < m.size(); i++) { elements[i] = new SystemProperty(varNames[i], m.get(varNames[i])); } } else { elements = new SystemProperty[0]; } return elements; } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput == null){ return; } if (viewer instanceof TableViewer){ TableViewer tableViewer= (TableViewer) viewer; if (tableViewer.getTable().isDisposed()) { return; } tableViewer.setComparator(new ViewerComparator() { public int compare(Viewer iviewer, Object e1, Object e2) { if (e1 == null) { return -1; } else if (e2 == null) { return 1; } else { return ((SystemProperty)e1).getName().compareToIgnoreCase(((SystemProperty)e2).getName()); } } }); } } } /** * Label provider for the environment table */ private class SystemPropertyLabelProvider extends LabelProvider implements ITableLabelProvider { public Image getColumnImage(Object element, int columnIndex) { if (columnIndex == 0) { return GrailsUiImages.getImage(GrailsUiImages.IMG_OBJ_GRAILS); } return null; } public String getColumnText(Object element, int columnIndex) { String result = null; if (element instanceof SystemProperty) { switch (columnIndex) { case 0: // variable result = ((SystemProperty) element).getName(); break; case 1: // value result = ((SystemProperty) element).getValue(); break; } } return result; } } private static final String P_VALUE = "value"; //$NON-NLS-1$ private static final String P_VARIABLE = "variable"; //$NON-NLS-1$ private Text commandTimeOut; private Text commandOutputLimit; private Button envAddButton; private Button envEditButton; private TableViewer environmentTable; private Button envRemoveButton; private String[] envTableColumnHeaders = { P_VARIABLE, P_VALUE, }; private Button keepGrailsRunning; private Button cleanGrails20output; private Text jvmArgs; /** * Attempts to add the given variable. Returns whether the variable * was added or not (as when the user answers not to overwrite an * existing variable). * @param variable the variable to add * @return whether the variable was added */ private boolean addVariable(SystemProperty variable) { String name= variable.getName(); TableItem[] items = environmentTable.getTable().getItems(); for (int i = 0; i < items.length; i++) { SystemProperty existingVariable = (SystemProperty) items[i].getData(); if (existingVariable.getName().equals(name)) { boolean overWrite= MessageDialog.openQuestion(getShell(), LaunchConfigurationsMessages.EnvironmentTab_12, MessageFormat.format(LaunchConfigurationsMessages.EnvironmentTab_13, new String[] {name})); // if (!overWrite) { return false; } environmentTable.remove(existingVariable); break; } } environmentTable.add(variable); return true; } protected boolean validate() { //Timeout value try { int value = getTimeOutValue(); if (value<5000) { setErrorMessage("Set command timeout value to at least 5000 milliseconds"); return false; } } catch (NumberFormatException e) { setErrorMessage("Command timeout must be an integer"); return false; } //Output limit try { int value = getOutputLimit(); if (value<5000) { setErrorMessage("Set command output limit value to at least 5000 characters"); return false; } } catch (NumberFormatException e) { setErrorMessage("Command output limit must be an integer"); return false; } setErrorMessage(null); return true; } @SuppressWarnings("restriction") @Override protected Control createContents(Composite _parent) { GridDataFactory grabHor = GridDataFactory.fillDefaults().grab(true, false); GridDataFactory grab = GridDataFactory.fillDefaults().grab(true, true); Composite parent = SWTFactory.createComposite(_parent, 1, 1, GridData.FILL_HORIZONTAL); // Create main composite Composite mainComposite = SWTFactory.createComposite(parent, 2, 1, GridData.FILL_HORIZONTAL); grab.applyTo(parent); grab.applyTo(mainComposite); createEnvironmentTable(mainComposite); createTableButtons(mainComposite); environmentTable.setInput(GrailsCoreActivator.getDefault().getUserSupliedLaunchSystemProperties()); grab.applyTo(environmentTable.getControl()); keepGrailsRunning = SWTFactory.createCheckButton(mainComposite, "Keep external Grails running", null, GrailsCoreActivator.getDefault().getKeepRunning(), 2); keepGrailsRunning.setToolTipText( "Try to reuse an existing Grails process to execute multiple commands.\n" + "This speeds up execution of Grails commands."); cleanGrails20output = SWTFactory.createCheckButton(mainComposite, "Cleanup Grails 2.0 Output", null, GrailsCoreActivator.getDefault().getCleanOutput(), 2); Composite morePrefs = SWTFactory.createComposite(parent, 2, 1, SWT.NONE); grabHor.applyTo(morePrefs); SWTFactory.createLabel(morePrefs, "Default JVM Args", 1); jvmArgs = SWTFactory.createText(morePrefs, SWT.BORDER, 1); jvmArgs.setToolTipText("Default arguments to pass to the JVM used for running Grails." + "Note that if an individual launch configuration " + "defines its own JVM arguments than these defaults will be ignored." ); grabHor.applyTo(jvmArgs); String jvmArgsStr = GrailsCoreActivator.getDefault().getJVMArgs(); if (jvmArgsStr!=null) { jvmArgs.setText(jvmArgsStr); } jvmArgs.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { boolean valid = validate(); setValid(valid); } }); SWTFactory.createLabel(morePrefs, "Grails Command Timeout [ms]:", 1); commandTimeOut = SWTFactory.createText(morePrefs, SWT.BORDER, 1); grabHor.applyTo(commandTimeOut); commandTimeOut.setText(""+GrailsCoreActivator.getDefault().getGrailsCommandTimeOut()); commandTimeOut.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { boolean valid = validate(); setValid(valid); } }); //GridDataFactory.fillDefaults().grab(false, false).hint(150,SWT.DEFAULT).applyTo(commandTimeOut); SWTFactory.createLabel(morePrefs, "Grails Command Output Limit [chars]:", 1); commandOutputLimit = SWTFactory.createText(morePrefs, SWT.BORDER, 1); grabHor.applyTo(commandOutputLimit); commandOutputLimit.setText(""+GrailsCoreActivator.getDefault().getGrailsCommandOutputLimit()); commandOutputLimit.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { boolean valid = validate(); setValid(valid); } }); //GridDataFactory.fillDefaults().grab(false, false).hint(150,SWT.DEFAULT).applyTo(commandTimeOut); return parent; } /** * Creates and configures the table that displayed the key/value * pairs that comprise the environment. * @param parent the composite in which the table should be created */ private void createEnvironmentTable(Composite parent) { Font font = parent.getFont(); // Create label, add it to the parent to align the right side buttons with the top of the table SWTFactory.createLabel(parent, "System properties to be passed to the Grails process", 2); // Create table composite Composite tableComposite = SWTFactory.createComposite(parent, font, 1, 1, GridData.FILL_BOTH, 0, 0); // Create table environmentTable = new TableViewer(tableComposite, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION); Table table = environmentTable.getTable(); table.setLayout(new GridLayout()); table.setLayoutData(new GridData(GridData.FILL_BOTH)); table.setHeaderVisible(true); table.setLinesVisible(true); table.setFont(font); environmentTable.setContentProvider(new SystemPropertyContentProvider()); environmentTable.setLabelProvider(new SystemPropertyLabelProvider()); environmentTable.setColumnProperties(new String[] {P_VARIABLE, P_VALUE}); environmentTable.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { handleTableSelectionChanged(event); } }); environmentTable.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { if (!environmentTable.getSelection().isEmpty()) { handleEnvEditButtonSelected(); } } }); // Create columns final TableColumn tc1 = new TableColumn(table, SWT.NONE, 0); tc1.setText(envTableColumnHeaders[0]); final TableColumn tc2 = new TableColumn(table, SWT.NONE, 1); tc2.setText(envTableColumnHeaders[1]); final Table tref = table; final Composite comp = tableComposite; tableComposite.addControlListener(new ControlAdapter() { public void controlResized(ControlEvent e) { Rectangle area = comp.getClientArea(); Point size = tref.computeSize(SWT.DEFAULT, SWT.DEFAULT); ScrollBar vBar = tref.getVerticalBar(); int width = area.width - tref.computeTrim(0,0,0,0).width - 2; if (size.y > area.height + tref.getHeaderHeight()) { Point vBarSize = vBar.getSize(); width -= vBarSize.x; } Point oldSize = tref.getSize(); if (oldSize.x > area.width) { tc1.setWidth(width/2-1); tc2.setWidth(width - tc1.getWidth()); tref.setSize(area.width, area.height); } else { tref.setSize(area.width, area.height); tc1.setWidth(width/2-1); tc2.setWidth(width - tc1.getWidth()); } } }); } /** * Creates and returns a new push button with the given * label and/or image. * * @param parent parent control * @param label button label or <code>null</code> * @param image image of <code>null</code> * * @return a new push button */ private Button createPushButton(Composite parent, String label, Image image) { return SWTFactory.createPushButton(parent, label, image); } /** * Creates the add/edit/remove buttons for the environment table * @param parent the composite in which the buttons should be created */ private void createTableButtons(Composite parent) { // Create button composite Composite buttonComposite = SWTFactory.createComposite(parent, parent.getFont(), 1, 1, GridData.VERTICAL_ALIGN_BEGINNING | GridData.HORIZONTAL_ALIGN_END, 0, 0); // Create buttons envAddButton = createPushButton(buttonComposite, LaunchConfigurationsMessages.EnvironmentTab_New_4, null); envAddButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { handleEnvAddButtonSelected(); } }); envEditButton = createPushButton(buttonComposite, LaunchConfigurationsMessages.EnvironmentTab_Edit_5, null); envEditButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { handleEnvEditButtonSelected(); } }); envEditButton.setEnabled(false); envRemoveButton = createPushButton(buttonComposite, LaunchConfigurationsMessages.EnvironmentTab_Remove_6, null); envRemoveButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { handleEnvRemoveButtonSelected(); } }); envRemoveButton.setEnabled(false); } private boolean getKeepGrailsRunning() { return keepGrailsRunning.getSelection(); } private int getTimeOutValue() throws NumberFormatException { return Integer.valueOf(commandTimeOut.getText()); } private int getOutputLimit() throws NumberFormatException { return Integer.valueOf(commandOutputLimit.getText()); } /** * Adds a new environment variable to the table. */ private void handleEnvAddButtonSelected() { MultipleInputDialog dialog = new MultipleInputDialog(getShell(), "System property") { @Override public void createBrowseField(String labelText, String initialValue, boolean allowEmpty) { super.createBrowseField(labelText, initialValue, allowEmpty); } @Override public void createVariablesField(String labelText, String initialValue, boolean allowEmpty) { Label label = new Label(panel, SWT.NONE); label.setText(labelText); label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); Composite comp = new Composite(panel, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight=0; layout.marginWidth=0; comp.setLayout(layout); comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); final Text text = new Text(comp, SWT.SINGLE | SWT.BORDER); GridData data = new GridData(GridData.FILL_HORIZONTAL); data.widthHint = 200; text.setLayoutData(data); text.setData(FIELD_NAME, labelText); // make sure rows are the same height on both panels. label.setSize(label.getSize().x, text.getSize().y); if (initialValue != null) { text.setText(initialValue); } if (!allowEmpty) { validators.add(new Validator() { public boolean validate() { return !text.getText().equals(IInternalDebugCoreConstants.EMPTY_STRING); } }); text.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { validateFields(); } }); } controlList.add(text); } }; dialog.addTextField(P_VARIABLE, null, false); dialog.addVariablesField(P_VALUE, null, true); if (dialog.open() != Window.OK) { return; } String name = dialog.getStringValue(P_VARIABLE); String value = dialog.getStringValue(P_VALUE); if (name != null && value != null && name.length() > 0 && value.length() >0) { addVariable(new SystemProperty(name.trim(), value.trim())); } } /** * Creates an editor for the value of the selected environment variable. */ private void handleEnvEditButtonSelected() { IStructuredSelection sel= (IStructuredSelection) environmentTable.getSelection(); SystemProperty var= (SystemProperty) sel.getFirstElement(); if (var == null) { return; } String originalName= var.getName(); String value= var.getValue(); MultipleInputDialog dialog= new MultipleInputDialog(getShell(), LaunchConfigurationsMessages.EnvironmentTab_11); dialog.addTextField(P_VARIABLE, originalName, false); dialog.addVariablesField(P_VALUE, value, true); if (dialog.open() != Window.OK) { return; } String name= dialog.getStringValue(P_VARIABLE); value= dialog.getStringValue(P_VALUE); if (!originalName.equals(name)) { if (addVariable(new SystemProperty(name, value))) { environmentTable.remove(var); } } else { var.setValue(value); environmentTable.update(var, null); } } /** * Removes the selected environment variable from the table. */ private void handleEnvRemoveButtonSelected() { IStructuredSelection sel = (IStructuredSelection) environmentTable.getSelection(); environmentTable.getControl().setRedraw(false); for (Iterator i = sel.iterator(); i.hasNext(); ) { SystemProperty var = (SystemProperty) i.next(); environmentTable.remove(var); } environmentTable.getControl().setRedraw(true); } /** * Responds to a selection changed event in the environment table * @param event the selection change event */ private void handleTableSelectionChanged(SelectionChangedEvent event) { int size = ((IStructuredSelection)event.getSelection()).size(); envEditButton.setEnabled(size == 1); envRemoveButton.setEnabled(size > 0); } public void init(IWorkbench workbench) { } @Override protected void performDefaults() { super.performDefaults(); GrailsCoreActivator.getDefault().setUserSupliedLaunchSystemProperties(Collections.EMPTY_MAP); GrailsCoreActivator.getDefault().setKeepGrailsRunning(GrailsCoreActivator.DEFAULT_KEEP_RUNNING_PREFERENCE); GrailsCoreActivator.getDefault().setJVMArgs(GrailsCoreActivator.DEFAULT_JVM_ARGS_PREFERENCE); environmentTable.setInput(GrailsCoreActivator.getDefault().getUserSupliedLaunchSystemProperties()); cleanGrails20output.setSelection(GrailsCoreActivator.DEFAULT_CLEAN_OUTPUT_PREFERENCE); } @Override public boolean performOk() { TableItem[] items = environmentTable.getTable().getItems(); Map<String,String> map = new HashMap<String, String>(); for (TableItem item : items) { SystemProperty prop = (SystemProperty) item.getData(); map.put(prop.getName(), prop.getValue()); } final GrailsCoreActivator grailsCore = GrailsCoreActivator.getDefault(); grailsCore.setUserSupliedLaunchSystemProperties(map); grailsCore.setKeepGrailsRunning(getKeepGrailsRunning()); grailsCore.setGrailsCommandTimeOut(getTimeOutValue()); grailsCore.setGrailsCommandOutputLimit(getOutputLimit()); grailsCore.setCleanOutput(getCleanOutput()); grailsCore.setJVMArgs(getJVMArgs()); return true; } private String getJVMArgs() { if (jvmArgs!=null && !jvmArgs.isDisposed()) { String args = jvmArgs.getText().trim(); if (!"".equals(args)) { return args; } } return null; } private boolean getCleanOutput() { return cleanGrails20output.getSelection(); } }