package org.marketcetera.photon.internal.strategy.engine.ui; import org.apache.commons.lang.StringUtils; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.databinding.viewers.IViewerObservable; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.marketcetera.photon.commons.Validate; import org.marketcetera.photon.commons.ui.databinding.DataBindingUtils; import org.marketcetera.photon.commons.ui.databinding.ObservingComposite; import org.marketcetera.photon.strategy.engine.model.core.Strategy; import org.marketcetera.photon.strategy.engine.model.core.StrategyEngine; import org.marketcetera.photon.strategy.engine.model.core.StrategyEngineCorePackage; import org.marketcetera.photon.strategy.engine.ui.ScriptSelectionButton; import org.marketcetera.photon.strategy.engine.ui.StrategyEnginesLabelProvider; import org.marketcetera.util.misc.ClassVersion; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /* $License$ */ /** * A control for capturing strategy deployment parameters. The UI is bound to * model objects passed in the constructor. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: DeployStrategyComposite.java 16154 2012-07-14 16:34:05Z colin $ * @since 2.0.0 */ @ClassVersion("$Id: DeployStrategyComposite.java 16154 2012-07-14 16:34:05Z colin $") public class DeployStrategyComposite extends ObservingComposite { private final Strategy mStrategy; private final DataBindingContext mDataBindingContext; /** * Constructor. Creates the UI widgets and binds them to the provided model. * * @param parent * parent composite in which to create the widgets * @param dataBindingContext * the data binding context to use for model-UI bindings * @param strategy * the strategy model object * @param availableEngines * the engines available for selection * @param selectedEngine * the selected engine model * @param scriptSelectionButtons * controls the creation of script selection buttons */ public DeployStrategyComposite(Composite parent, DataBindingContext dataBindingContext, final Strategy strategy, StrategyEngine[] availableEngines, final IObservableValue selectedEngine, ScriptSelectionButton... scriptSelectionButtons) { super(parent); mDataBindingContext = dataBindingContext; mStrategy = strategy; GridLayoutFactory.swtDefaults().spacing(10, 5).numColumns(3).applyTo( this); Messages.STRATEGY_DEPLOYMENT_COMPOSITE_SCRIPT.createLabel(this); final IObservableValue script = observe(StrategyEngineCorePackage.Literals.STRATEGY__SCRIPT_PATH); { Text text = new Text(this, SWT.BORDER); GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab( true, false).applyTo(text); bindRequiredField(SWTObservables.observeText(text, SWT.Modify), script, Messages.STRATEGY_DEPLOYMENT_COMPOSITE_SCRIPT .getRawLabel()); script.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { scriptValueChanged(); } }); scriptValueChanged(); } { final Composite buttons = new Composite(this, SWT.NONE); for (ScriptSelectionButton scriptSelectionButton : scriptSelectionButtons) { final ScriptSelectionButton current = scriptSelectionButton; Button button = new Button(buttons, SWT.NONE); button.setText(scriptSelectionButton.getText()); button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { final String selected = current.selectScript( getShell(), (String) script.getValue()); if (StringUtils.isNotBlank(selected)) { script.setValue(selected.trim()); } } }); } GridDataFactory.fillDefaults().applyTo(buttons); GridLayoutFactory.swtDefaults().numColumns( scriptSelectionButtons.length).generateLayout(buttons); } Messages.STRATEGY_DEPLOYMENT_COMPOSITE_LANGUAGE.createLabel(this); { Combo combo = new Combo(this, SWT.NONE); combo.setItems(Language.getValues()); GridDataFactory.swtDefaults().align(SWT.LEFT, SWT.CENTER).hint(100, SWT.DEFAULT).applyTo(combo); bindRequiredField( SWTObservables.observeText(combo), observe(StrategyEngineCorePackage.Literals.STRATEGY__LANGUAGE), Messages.STRATEGY_DEPLOYMENT_COMPOSITE_LANGUAGE .getRawLabel()); } new Label(this, SWT.NONE); Messages.STRATEGY_DEPLOYMENT_COMPOSITE_CLASS.createLabel(this); { Text classText = new Text(this, SWT.BORDER); GridDataFactory.swtDefaults().align(SWT.LEFT, SWT.CENTER).hint(180, SWT.DEFAULT).applyTo(classText); bindRequiredField( SWTObservables.observeText(classText, SWT.Modify), observe(StrategyEngineCorePackage.Literals.STRATEGY__CLASS_NAME), Messages.STRATEGY_DEPLOYMENT_COMPOSITE_CLASS.getRawLabel()); } new Label(this, SWT.NONE); Messages.STRATEGY_DEPLOYMENT_COMPOSITE_INSTANCE_NAME.createLabel(this); { Text instanceNameText = new Text(this, SWT.BORDER); GridDataFactory.swtDefaults().align(SWT.LEFT, SWT.CENTER).hint(180, SWT.DEFAULT).applyTo(instanceNameText); bindRequiredField( SWTObservables.observeText(instanceNameText, SWT.Modify), observe(StrategyEngineCorePackage.Literals.STRATEGY__INSTANCE_NAME), Messages.DEPLOYED_STRATEGY_CONFIGURATION_COMPOSITE_INSTANCE_NAME.getRawLabel()); } new Label(this, SWT.NONE); { Group configurationGroup = new Group(this, SWT.NONE); GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab( true, false).indent(5, SWT.DEFAULT).span(3, 1).applyTo( configurationGroup); GridLayoutFactory.swtDefaults().applyTo(configurationGroup); configurationGroup .setText(Messages.STRATEGY_DEPLOYMENT_COMPOSITE_CONFIGURATION_GROUP__LABEL .getText()); { Button routeButton = new Button(configurationGroup, SWT.CHECK); routeButton .setText(Messages.STRATEGY_DEPLOYMENT_COMPOSITE_ROUTE .getRawLabel()); routeButton .setToolTipText(Messages.STRATEGY_DEPLOYMENT_COMPOSITE_ROUTE .getTooltip()); dataBindingContext .bindValue( SWTObservables.observeSelection(routeButton), observe(StrategyEngineCorePackage.Literals.STRATEGY__ROUTE_ORDERS_TO_SERVER)); } } { Label selectionEnginesLabel = new Label(this, SWT.NONE); GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab( true, false).span(3, 1).applyTo(selectionEnginesLabel); selectionEnginesLabel .setText(Messages.STRATEGY_DEPLOYMENT_COMPOSITE_ENGINE_SELECTION_TABLE__LABEL .getText()); } { final CheckboxTableViewer selectEnginesTableViewer = CheckboxTableViewer .newCheckList(this, SWT.BORDER); GridDataFactory.fillDefaults().grab(true, true).indent(6, SWT.DEFAULT).span(3, 1).applyTo( selectEnginesTableViewer.getTable()); selectEnginesTableViewer .setContentProvider(new ArrayContentProvider()); selectEnginesTableViewer .setLabelProvider(new StrategyEnginesLabelProvider()); selectEnginesTableViewer.setInput(availableEngines); /* * Typically, checkbox tables allow multiple selection. In this * case, we are using the widget for a single selection so we need a * custom listener. It is also a WritableValue and IViewerObservable * for data binding and RequiredFieldSupport. */ class FirstAndOnly extends WritableValue implements IViewerObservable, ICheckStateListener { public FirstAndOnly() { super(selectedEngine.getValue(), StrategyEngine.class); } @Override public Viewer getViewer() { return selectEnginesTableViewer; } @Override public void checkStateChanged(CheckStateChangedEvent event) { // simulate single selection if (event.getChecked()) { for (Object object : selectEnginesTableViewer .getCheckedElements()) { if (object != event.getElement()) { selectEnginesTableViewer.setChecked(object, false); } else { setValue(object); } } } else { setValue(null); } } } // if an engine has been pre-selected, check it and disable the // table if (selectedEngine.getValue() != null) { selectEnginesTableViewer.getTable().setEnabled(false); selectEnginesTableViewer.setChecked(selectedEngine.getValue(), true); } FirstAndOnly firstAndOnly = new FirstAndOnly(); selectEnginesTableViewer.addCheckStateListener(firstAndOnly); getObservablesManager().addObservable(firstAndOnly); bindRequiredField(firstAndOnly, selectedEngine, Messages.STRATEGY_DEPLOYMENT_COMPOSITE_ENGINE__LABEL .getText()); } } private void bindRequiredField(IObservableValue target, final IObservableValue model, final String description) { DataBindingUtils.bindRequiredField(mDataBindingContext, target, model, description); } private IObservableValue observe(EStructuralFeature feature) { return DataBindingUtils.observeAndTrack(getObservablesManager(), mStrategy, feature); } private void scriptValueChanged() { final String newValue = mStrategy.getScriptPath(); if (newValue != null) { /* * Split on '.' to get the extension */ String[] split1 = newValue.split("\\.", -1); //$NON-NLS-1$ if (split1.length > 1) { Language language = Language .getLanguageFor(split1[split1.length - 1]); if (language != null) { mStrategy.setLanguage(language.getValue()); /* * Split on common path separators, '\' or '/' to try to get * the file name. Don't just use File.separatorChar since * there is no guarantee that this is a native file path. */ String[] split2 = newValue.substring(0, newValue.lastIndexOf('.')).split("[\\\\\\/]", -1); //$NON-NLS-1$ if (split2.length > 1) { String className = language .getClassFor(split2[split2.length - 1]); if (className != null) { mStrategy.setClassName(className); } } } } } } /** * Encapsulates some script language related logic/heuristics. */ private enum Language { /** * Java */ Java("JAVA", "java"), //$NON-NLS-1$ //$NON-NLS-2$ /** * Ruby */ Ruby("RUBY", "rb") { //$NON-NLS-1$ //$NON-NLS-2$ @Override public String getClassFor(String fileNameExtensionStripped) { String className = super.getClassFor(fileNameExtensionStripped); /* * retains letters and numbers and capitalizes the first in a * word, e.g. my_strategy -> MyStrategy */ StringBuilder builder = new StringBuilder(); boolean caps = true; for (char c : className.toCharArray()) { if (Character.isLetterOrDigit(c)) { builder.append(caps ? Character.toUpperCase(c) : c); caps = false; } else { caps = true; } } return StringUtils.defaultIfEmpty(builder.toString(), null); } }; private final String mValue; private final String mExtension; private static final ImmutableMap<String, Language> sExtensionToLanguage; private static final ImmutableList<String> sValues; static { ImmutableMap.Builder<String, Language> mapBuilder = ImmutableMap .builder(); ImmutableList.Builder<String> valuesBuilder = ImmutableList .builder(); for (Language language : Language.values()) { mapBuilder.put(language.mExtension, language); valuesBuilder.add(language.mValue); } sExtensionToLanguage = mapBuilder.build(); sValues = valuesBuilder.build(); } /** * Returns the language associated with an extension. * * @param extension * the extension * @return the language */ public static Language getLanguageFor(String extension) { return sExtensionToLanguage.get(extension); } /** * Returns all language values. * * @return the values */ public static String[] getValues() { return sValues.toArray(new String[sValues.size()]); } private Language(String value, String extension) { mValue = value; mExtension = extension; } /** * Returns a best-guess class name for a given file name. The base * implementation discards characters as needed to make the string a * valid Java identifier. * * @param fileNameExtensionStripped * the file name without its extension * @return the guessed class name * @throws IllegalArgumentException * if fileNameExtensionStripped is null */ public String getClassFor(String fileNameExtensionStripped) { Validate.notNull(fileNameExtensionStripped, "fileNameExtensionStripped"); //$NON-NLS-1$ StringBuilder builder = new StringBuilder(); boolean started = false; for (char c : fileNameExtensionStripped.toCharArray()) { if (started) { if (Character.isJavaIdentifierPart(c)) { builder.append(c); } } else if (Character.isJavaIdentifierStart(c)) { builder.append(c); started = true; } } return builder.toString(); } /** * Returns the value (in the data model domain) for this language. * * @return the value */ public String getValue() { return mValue; } } }