/** * Copyright (c) 2010, 2013 Darmstadt University of Technology. * 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: * Stefan Prisca - initial API and implementation * Marcel Bruch - changed to use jface databinding * Olav Lenz - clean up metadata page. */ package org.eclipse.recommenders.internal.snipmatch.rcp.editors; import static com.google.common.base.Strings.isNullOrEmpty; import static org.eclipse.recommenders.internal.snipmatch.rcp.Constants.HELP_URL; import static org.eclipse.recommenders.snipmatch.Location.*; import static org.eclipse.recommenders.utils.Checks.cast; import java.text.MessageFormat; import java.util.Collection; import java.util.Set; import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.eclipse.core.commands.Command; import org.eclipse.core.commands.Parameterization; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.IChangeListener; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.set.IObservableSet; import org.eclipse.core.databinding.observable.value.ComputedValue; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.property.INativePropertyListener; import org.eclipse.core.databinding.property.ISimplePropertyListener; import org.eclipse.core.databinding.property.value.SimpleValueProperty; import org.eclipse.core.internal.databinding.property.value.SelfValueProperty; import org.eclipse.jface.action.Action; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.databinding.viewers.ViewerProperties; import org.eclipse.jface.databinding.viewers.ViewerSupport; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.ListViewer; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.recommenders.coordinates.ProjectCoordinate; import org.eclipse.recommenders.injection.InjectionService; import org.eclipse.recommenders.internal.models.rcp.ProjectCoordinateSelectionDialog; import org.eclipse.recommenders.internal.snipmatch.rcp.SnippetsView; import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.LogMessages; import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.Messages; import org.eclipse.recommenders.rcp.SharedImages; import org.eclipse.recommenders.rcp.utils.DatabindingConverters.EnumToBooleanConverter; import org.eclipse.recommenders.rcp.utils.DatabindingConverters.StringToUuidConverter; import org.eclipse.recommenders.rcp.utils.ObjectToBooleanConverter; import org.eclipse.recommenders.snipmatch.ISnippet; import org.eclipse.recommenders.snipmatch.Location; import org.eclipse.recommenders.snipmatch.Snippet; import org.eclipse.recommenders.snipmatch.rcp.SnippetEditorInput; import org.eclipse.recommenders.utils.Logs; import org.eclipse.recommenders.utils.rcp.Browsers; import org.eclipse.recommenders.utils.rcp.Selections; import org.eclipse.recommenders.utils.rcp.preferences.AbstractLinkContributionPage; import org.eclipse.swt.SWT; 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.events.SelectionListener; import org.eclipse.swt.graphics.Image; 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.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.List; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.forms.AbstractFormPart; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.forms.editor.FormPage; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.handlers.IHandlerService; import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @SuppressWarnings("restriction") public class SnippetMetadataPage extends FormPage { private static final String EXTENSION_DISCOVERY_COMMAND_ID = "org.eclipse.recommenders.rcp.commands.extensionDiscovery"; //$NON-NLS-1$ private final class ContentsPartDirtyListener implements IChangeListener { @Override public void handleChange(ChangeEvent event) { contentsPart.markDirty(); } } private static final String SNIPMATCH_P2_DISCOVERY_URL = "https://www.eclipse.org/downloads/download.php?r=1&file=/recommenders/discovery/2.x/directories/snipmatch.xml"; //$NON-NLS-1$ private static final Location[] SNIPMATCH_LOCATIONS = { FILE, JAVA_FILE, JAVA, JAVA_STATEMENTS, JAVA_TYPE_MEMBERS, JAVADOC }; public static final String TEXT_SNIPPETNAME = "org.eclipse.recommenders.snipmatch.rcp.snippetmetadatapage.snippetname"; //$NON-NLS-1$ private ISnippet snippet; private AbstractFormPart contentsPart; private Text txtName; private Text txtDescription; private ComboViewer comboLocation; private Text txtUuid; private ListViewer listViewerFilenameRestrictions; private ListViewer listViewerExtraSearchTerms; private ListViewer listViewerTags; private ListViewer listViewerDependencies; private Composite filenameRestrictionsButtonContainer; private Composite extraSearchTermsButtonContainer; private Composite tagsButtonContainer; private Composite dependenciesButtonContainer; private Button btnAddFilenameRestriction; private Button btnRemoveFilenameRestriction; private Button btnRemoveExtraSearchTerm; private Button btnRemoveTag; private Button btnRemoveDependency; private IObservableSet modelSnippetDependencies; private IObservableList modelSnippetExtraSearchTerms; private IObservableList modelSnippetFilenameRestrictions; private IObservableList modelSnippetTags; private DataBindingContext context; private final Image errorDecorationImage = FieldDecorationRegistry.getDefault() .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage(); private final Image infoDecorationImage = FieldDecorationRegistry.getDefault() .getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION).getImage(); public SnippetMetadataPage(FormEditor editor, String id, String title) { super(editor, id, title); } @Override protected void createFormContent(IManagedForm managedForm) { FormToolkit toolkit = managedForm.getToolkit(); ScrolledForm form = managedForm.getForm(); createHeader(form); Composite body = form.getBody(); toolkit.decorateFormHeading(form.getForm()); toolkit.paintBordersFor(body); managedForm.getForm().getBody().setLayout(new GridLayout(3, false)); contentsPart = new AbstractFormPart() { @Override public void initialize(IManagedForm managedForm) { super.initialize(managedForm); int horizontalIndent = errorDecorationImage.getBounds().width + 2; createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_NAME); txtName = createTextField(managedForm, snippet.getName(), Messages.EDITOR_TEXT_MESSAGE_SNIPPET_NAME, horizontalIndent); txtName.setData(SnippetsView.SWT_ID, TEXT_SNIPPETNAME); final ControlDecoration nameDecoration = createDecoration(txtName, Messages.ERROR_SNIPPET_NAME_CANNOT_BE_EMPTY, errorDecorationImage, SWT.LEFT); txtName.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent event) { if (isNullOrEmpty(txtName.getText())) { nameDecoration.show(); } else { nameDecoration.hide(); } } }); createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_DESCRIPTION); txtDescription = createTextField(managedForm, snippet.getDescription(), Messages.EDITOR_TEXT_MESSAGE_SNIPPET_DESCRIPTION, horizontalIndent); createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_LOCATION); comboLocation = new ComboViewer(managedForm.getForm().getBody(), SWT.DROP_DOWN | SWT.READ_ONLY); managedForm.getToolkit().adapt(comboLocation.getCombo(), true, true); comboLocation.setContentProvider(ArrayContentProvider.getInstance()); comboLocation.setInput(SNIPMATCH_LOCATIONS); comboLocation.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { if (element instanceof Location) { Location location = (Location) element; switch (location) { case FILE: return Messages.SNIPMATCH_LOCATION_FILE; case JAVA_FILE: return Messages.SNIPMATCH_LOCATION_JAVA_FILE; case JAVA: return Messages.SNIPMATCH_LOCATION_JAVA; case JAVA_STATEMENTS: return Messages.SNIPMATCH_LOCATION_JAVA_STATEMENTS; case JAVA_TYPE_MEMBERS: return Messages.SNIPMATCH_LOCATION_JAVA_MEMBERS; case JAVADOC: return Messages.SNIPMATCH_LOCATION_JAVADOC; default: break; } } return super.getText(element); } }); comboLocation.getCombo().setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER) .grab(true, false).span(2, 1).indent(horizontalIndent, 0).create()); final ControlDecoration locationErrorDecoration = createDecoration(comboLocation.getCombo(), Messages.ERROR_SNIPPET_LOCATION_CANNOT_BE_EMPTY + "\n" //$NON-NLS-1$ + Messages.EDITOR_DESCRIPTION_LOCATION, errorDecorationImage, SWT.LEFT); final ControlDecoration locationDescriptionDecoration = createDecoration(comboLocation.getCombo(), Messages.EDITOR_DESCRIPTION_LOCATION, infoDecorationImage, SWT.LEFT); comboLocation.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { if (event.getSelection().isEmpty()) { locationErrorDecoration.show(); locationDescriptionDecoration.hide(); } else { locationErrorDecoration.hide(); locationDescriptionDecoration.show(); } } }); comboLocation.setSelection(new StructuredSelection(snippet.getLocation())); createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_FILENAME_RESTRICTIONS); listViewerFilenameRestrictions = createListViewer(managedForm, horizontalIndent); createDecoration(listViewerFilenameRestrictions.getList(), Messages.EDITOR_DESCRIPTION_FILENAME_RESTRICTIONS, infoDecorationImage, SWT.TOP | SWT.LEFT); filenameRestrictionsButtonContainer = createButtonContainer(managedForm); btnAddFilenameRestriction = createButton(managedForm, filenameRestrictionsButtonContainer, Messages.EDITOR_BUTTON_ADD, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { createFilenameRestrictionInputDialog(filenameRestrictionsButtonContainer.getShell()) .open(); } }); btnAddFilenameRestriction.setEnabled(snippet.getLocation() == Location.FILE); btnRemoveFilenameRestriction = createButton(managedForm, filenameRestrictionsButtonContainer, Messages.EDITOR_BUTTON_REMOVE, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Optional<String> o = Selections.getFirstSelected(listViewerFilenameRestrictions); if (o.isPresent()) { modelSnippetFilenameRestrictions.remove(o.get()); } } }); createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_EXTRA_SEARCH_TERMS); listViewerExtraSearchTerms = createListViewer(managedForm, horizontalIndent); createDecoration(listViewerExtraSearchTerms.getList(), Messages.EDITOR_DESCRIPTION_EXTRA_SEARCH_TERMS, infoDecorationImage, SWT.TOP | SWT.LEFT); extraSearchTermsButtonContainer = createButtonContainer(managedForm); createButton(managedForm, extraSearchTermsButtonContainer, Messages.EDITOR_BUTTON_ADD, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { createExtraSearchTermInputDialog(extraSearchTermsButtonContainer.getShell()).open(); } }); btnRemoveExtraSearchTerm = createButton(managedForm, extraSearchTermsButtonContainer, Messages.EDITOR_BUTTON_REMOVE, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Optional<String> o = Selections.getFirstSelected(listViewerExtraSearchTerms); if (o.isPresent()) { modelSnippetExtraSearchTerms.remove(o.get()); } } }); createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_TAG); listViewerTags = createListViewer(managedForm, horizontalIndent); createDecoration(listViewerTags.getList(), Messages.EDITOR_DESCRIPTION_TAGS, infoDecorationImage, SWT.TOP | SWT.LEFT); tagsButtonContainer = createButtonContainer(managedForm); createButton(managedForm, tagsButtonContainer, Messages.EDITOR_BUTTON_ADD, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { createTagInputDialog(tagsButtonContainer.getShell()).open(); } }); btnRemoveTag = createButton(managedForm, tagsButtonContainer, Messages.EDITOR_BUTTON_REMOVE, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Optional<String> o = Selections.getFirstSelected(listViewerTags); if (o.isPresent()) { modelSnippetTags.remove(o.get()); } } }); createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_DEPENDENCIES); listViewerDependencies = createListViewer(managedForm, horizontalIndent); createDecoration(listViewerDependencies.getList(), Messages.EDITOR_DESCRIPTION_DEPENDENCIES, infoDecorationImage, SWT.TOP | SWT.LEFT); dependenciesButtonContainer = createButtonContainer(managedForm); createButton(managedForm, dependenciesButtonContainer, Messages.EDITOR_BUTTON_ADD, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Shell shell = dependenciesButtonContainer.getShell(); ProjectCoordinateSelectionDialog dialog = new ProjectCoordinateSelectionDialog(shell) { @Override public String createLabelForProjectCoordinate(ProjectCoordinate element) { return getStringForDependency(element); } private final Set<String> alreadyAddedPcLabels = Sets.newHashSet(); @Override public boolean filter(ProjectCoordinate pc) { for (String dependencylistItem : fetchDependencyListItems()) { if (dependencylistItem.equals(getStringForDependency(pc))) { return true; } } String labelForPc = createLabelForProjectCoordinate(pc); if (alreadyAddedPcLabels.contains(labelForPc)) { return true; } else { alreadyAddedPcLabels.add(labelForPc); return false; } } }; dialog.setInitialPattern(""); //$NON-NLS-1$ dialog.setTitle(Messages.DIALOG_TITLE_SELECT_DEPENDENCY); dialog.setMessage(Messages.DIALOG_MESSAGE_SELECT_DEPENDENCY); dialog.open(); Set<ProjectCoordinate> selectedElements = changeVersionsToZero( dialog.getSelectedElements()); modelSnippetDependencies.addAll(selectedElements); } }); btnRemoveDependency = createButton(managedForm, dependenciesButtonContainer, Messages.EDITOR_BUTTON_REMOVE, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Optional<String> o = Selections.getFirstSelected(listViewerDependencies); if (o.isPresent()) { modelSnippetDependencies.remove(o.get()); } } }); createLabel(managedForm, Messages.EDITOR_LABEL_SNIPPET_UUID); txtUuid = managedForm.getToolkit().createText(managedForm.getForm().getBody(), snippet.getUuid().toString(), SWT.READ_ONLY); txtUuid.setLayoutData( GridDataFactory.fillDefaults().grab(true, false).indent(horizontalIndent, 0).create()); } private void createLabel(IManagedForm managedForm, String text) { Label label = managedForm.getToolkit().createLabel(managedForm.getForm().getBody(), text, SWT.NONE); label.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1)); } private Text createTextField(IManagedForm managedForm, String value, String message, int horizontalIndent) { Text text = managedForm.getToolkit().createText(managedForm.getForm().getBody(), value, SWT.NONE); text.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false) .span(2, 1).indent(horizontalIndent, 0).create()); text.setMessage(message); return text; } private ControlDecoration createDecoration(Control control, String description, Image image, int style) { final ControlDecoration decoration = new ControlDecoration(control, style); decoration.setDescriptionText(description); decoration.setImage(image); decoration.setMarginWidth(1); return decoration; } private ListViewer createListViewer(IManagedForm managedForm, int horizontalIndent) { ListViewer listViewer = new ListViewer(managedForm.getForm().getBody(), SWT.BORDER | SWT.V_SCROLL); List lstFilenames = listViewer.getList(); lstFilenames.setLayoutData( GridDataFactory.fillDefaults().grab(true, true).indent(horizontalIndent, 0).create()); return listViewer; } private Composite createButtonContainer(IManagedForm managedForm) { Composite container = managedForm.getToolkit().createComposite(managedForm.getForm().getBody(), SWT.NONE); container.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, false, 1, 1)); managedForm.getToolkit().paintBordersFor(container); container.setLayout(new GridLayout(1, false)); return container; } private Button createButton(IManagedForm managedForm, Composite parent, String label, SelectionListener listener) { Button button = managedForm.getToolkit().createButton(parent, label, SWT.NONE); button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); button.addSelectionListener(listener); return button; } @Override public void commit(boolean onSave) { if (onSave) { super.commit(onSave); } } @Override public void refresh() { context.updateTargets(); super.refresh(); } }; managedForm.addPart(contentsPart); context = createDataBindingContext(); } private ScrolledForm createHeader(ScrolledForm form) { form.setText(Messages.EDITOR_TITLE_METADATA); SharedImages sharedImages = InjectionService.getInstance().getInjector().getInstance(SharedImages.class); Action openDiscoveryAction = new Action(Messages.EDITOR_EXTENSIONS_HEADER_EXT_LINK, sharedImages.getDescriptor(SharedImages.Images.ELCL_INSTALL_EXTENSIONS)) { @Override public void run() { showDiscoveryDialog(); }; }; EditorUtils.addActionToForm(form, openDiscoveryAction, Messages.EDITOR_EXTENSIONS_HEADER_EXT_LINK); Action showHelpAction = new Action(Messages.EDITOR_TOOLBAR_ITEM_HELP, sharedImages.getDescriptor(SharedImages.Images.ELCL_HELP)) { @Override public void run() { Browsers.tryOpenInExternalBrowser(HELP_URL); }; }; EditorUtils.addActionToForm(form, showHelpAction, Messages.EDITOR_TOOLBAR_ITEM_HELP); return form; } private Collection<String> fetchDependencyListItems() { final Collection<String> items = Lists.newArrayList(); Display.getDefault().syncExec(new Runnable() { @Override public void run() { for (String string : listViewerDependencies.getList().getItems()) { items.add(string); } } }); return items; } private Set<ProjectCoordinate> changeVersionsToZero(Set<ProjectCoordinate> resolved) { Set<ProjectCoordinate> result = Sets.newHashSet(); for (ProjectCoordinate projectCoordinate : resolved) { result.add( new ProjectCoordinate(projectCoordinate.getGroupId(), projectCoordinate.getArtifactId(), "0.0.0")); //$NON-NLS-1$ } return result; } private InputDialog createFilenameRestrictionInputDialog(Shell shell) { IInputValidator validator = new IInputValidator() { @Override public String isValid(String newText) { if (newText == null) { return ""; //$NON-NLS-1$ } if (StringUtils.isBlank(newText)) { return ""; //$NON-NLS-1$ } if (newText.contains("*")) { //$NON-NLS-1$ return MessageFormat .format(Messages.DIALOG_VALIDATOR_FILENAME_RESTRICTION_CONTAINS_ILLEGAL_CHARACTER, "*"); //$NON-NLS-1$ } if (snippet.getFilenameRestrictions().contains(newText.trim().toLowerCase())) { return Messages.DIALOG_VALIDATOR_FILENAME_RESTRICTION_ALREADY_ADDED; } return null; } }; return new InputDialog(shell, Messages.DIALOG_TITLE_ENTER_NEW_FILENAME_RESTRICTION, Messages.DIALOG_MESSAGE_ENTER_NEW_FILENAME_RESTRICTION, "", validator) { //$NON-NLS-1$ @Override protected void okPressed() { modelSnippetFilenameRestrictions.add(getValue().toLowerCase()); super.okPressed(); } }; } private InputDialog createExtraSearchTermInputDialog(Shell shell) { IInputValidator validator = new IInputValidator() { @Override public String isValid(String newText) { if (isNullOrEmpty(newText)) { return ""; //$NON-NLS-1$ } if (snippet.getExtraSearchTerms().contains(newText)) { return Messages.DIALOG_VALIDATOR_EXTRA_SEARCH_TERM_ALREADY_ADDED; } return null; } }; return new InputDialog(shell, Messages.DIALOG_TITLE_ENTER_NEW_EXTRA_SEARCH_TERM, Messages.DIALOG_MESSAGE_ENTER_NEW_EXTRA_SEARCH_TERM, "", validator) { //$NON-NLS-1$ @Override protected void okPressed() { modelSnippetExtraSearchTerms.add(getValue()); super.okPressed(); } }; } private InputDialog createTagInputDialog(Shell shell) { IInputValidator validator = new IInputValidator() { @Override public String isValid(String newText) { if (isNullOrEmpty(newText)) { return ""; //$NON-NLS-1$ } if (snippet.getTags().contains(newText)) { return Messages.DIALOG_VALIDATOR_TAG_ALREADY_ADDED; } return null; } }; return new InputDialog(shell, Messages.DIALOG_TITLE_ENTER_NEW_TAG, Messages.DIALOG_MESSAGE_ENTER_NEW_TAG, "", //$NON-NLS-1$ validator) { @Override protected void okPressed() { modelSnippetTags.add(getValue()); super.okPressed(); } }; } private DataBindingContext createDataBindingContext() { DataBindingContext context = new DataBindingContext(); // Name IObservableValue targetNameWidget = WidgetProperties.text(SWT.Modify).observe(txtName); IObservableValue modelSnippetName = BeanProperties.value(Snippet.class, "name", String.class).observe(snippet); //$NON-NLS-1$ context.bindValue(targetNameWidget, modelSnippetName); modelSnippetName.addChangeListener(new ContentsPartDirtyListener()); // Description IObservableValue targetDescriptionWidget = WidgetProperties.text(SWT.Modify).observe(txtDescription); IObservableValue modelSnippetDescription = BeanProperties.value(Snippet.class, "description", String.class) //$NON-NLS-1$ .observe(snippet); context.bindValue(targetDescriptionWidget, modelSnippetDescription); modelSnippetDescription.addChangeListener(new ContentsPartDirtyListener()); // Location IObservableValue targetLocationComboViewer = ViewerProperties.singleSelection().observe(comboLocation); IObservableValue modelSnippetLocation = BeanProperties.value(Snippet.class, "location", Location.class) //$NON-NLS-1$ .observe(snippet); context.bindValue(targetLocationComboViewer, modelSnippetLocation); modelSnippetLocation.addChangeListener(new ContentsPartDirtyListener()); // Filename restrictions modelSnippetFilenameRestrictions = BeanProperties.list(Snippet.class, "filenameRestrictions", String.class) //$NON-NLS-1$ .observe(snippet); modelSnippetFilenameRestrictions.addChangeListener(new ContentsPartDirtyListener()); ViewerSupport.bind(listViewerFilenameRestrictions, modelSnippetFilenameRestrictions, new FilenameRestrictionLabelProperty()); UpdateValueStrategy locationEnablementStrategy = new UpdateValueStrategy(); locationEnablementStrategy.setConverter(new NullSafeEnumToBooleanConverter(new Location[] { Location.FILE })); IObservableValue modelFilenameRestrictionsEnabled = WidgetProperties.enabled() .observe(listViewerFilenameRestrictions.getControl()); final IObservableValue modelAddFilenameRestrictionButtonEnabled = WidgetProperties.enabled() .observe(btnAddFilenameRestriction); context.bindValue(targetLocationComboViewer, modelFilenameRestrictionsEnabled, locationEnablementStrategy, null); context.bindValue(targetLocationComboViewer, modelAddFilenameRestrictionButtonEnabled, locationEnablementStrategy, null); final IObservableValue targetEnableRemoveFilenameButton = WidgetProperties.enabled() .observe(btnRemoveFilenameRestriction); final IObservableValue targetFilenameRestrictionSelected = ViewerProperties.singleSelection() .observe(listViewerFilenameRestrictions); ComputedValue computedRemoveButtonEnablement = new ObserveValueWithNullChecker(targetEnableRemoveFilenameButton, modelAddFilenameRestrictionButtonEnabled, targetFilenameRestrictionSelected); context.bindValue(targetEnableRemoveFilenameButton, computedRemoveButtonEnablement); // Extra search terms modelSnippetExtraSearchTerms = BeanProperties.list(Snippet.class, "extraSearchTerms", String.class) //$NON-NLS-1$ .observe(snippet); ViewerSupport.bind(listViewerExtraSearchTerms, modelSnippetExtraSearchTerms, new SelfValueProperty(String.class)); modelSnippetExtraSearchTerms.addChangeListener(new ContentsPartDirtyListener()); UpdateValueStrategy objectToBooleanStrategy = new UpdateValueStrategy(); objectToBooleanStrategy.setConverter(new ObjectToBooleanConverter()); IObservableValue targetExtraSearchTermSelected = ViewerProperties.singleSelection() .observe(listViewerExtraSearchTerms); IObservableValue modelRemoveExtraSearchTermButtonEnabled = WidgetProperties.enabled() .observe(btnRemoveExtraSearchTerm); context.bindValue(targetExtraSearchTermSelected, modelRemoveExtraSearchTermButtonEnabled, objectToBooleanStrategy, null); // Tags modelSnippetTags = BeanProperties.list(Snippet.class, "tags", String.class).observe(snippet); //$NON-NLS-1$ ViewerSupport.bind(listViewerTags, modelSnippetTags, new SelfValueProperty(String.class)); modelSnippetTags.addChangeListener(new ContentsPartDirtyListener()); IObservableValue targetTagSelected = ViewerProperties.singleSelection().observe(listViewerTags); IObservableValue modelRemoveTagButtonEnabled = WidgetProperties.enabled().observe(btnRemoveTag); context.bindValue(targetTagSelected, modelRemoveTagButtonEnabled, objectToBooleanStrategy, null); // Dependencies modelSnippetDependencies = BeanProperties.set(Snippet.class, "neededDependencies", ProjectCoordinate.class) //$NON-NLS-1$ .observe(snippet); ViewerSupport.bind(listViewerDependencies, modelSnippetDependencies, new SimpleValueProperty() { @Override public Object getValueType() { return ProjectCoordinate.class; } @Override protected Object doGetValue(Object source) { if (source != null) { ProjectCoordinate pc = cast(source); return getStringForDependency(pc); } return ""; //$NON-NLS-1$ } @Override protected void doSetValue(Object source, Object value) { } @Override public INativePropertyListener adaptListener(ISimplePropertyListener listener) { return null; } }); modelSnippetDependencies.addChangeListener(new ContentsPartDirtyListener()); IObservableValue targetDependencySelected = ViewerProperties.singleSelection().observe(listViewerDependencies); IObservableValue modelRemoveDependencyButtonEnabled = WidgetProperties.enabled().observe(btnRemoveDependency); context.bindValue(targetDependencySelected, modelRemoveDependencyButtonEnabled, objectToBooleanStrategy, null); // uuid text UpdateValueStrategy stringToUuidStrategy = new UpdateValueStrategy(); stringToUuidStrategy.setConverter(new StringToUuidConverter()); IObservableValue targetUuidWidget = WidgetProperties.text(SWT.Modify).observe(txtUuid); IObservableValue modelSnippetUuid = BeanProperties.value(Snippet.class, "uuid", UUID.class).observe(snippet); //$NON-NLS-1$ context.bindValue(targetUuidWidget, modelSnippetUuid, stringToUuidStrategy, null); modelSnippetUuid.addChangeListener(new ContentsPartDirtyListener()); context.updateModels(); return context; } String getStringForDependency(ProjectCoordinate pc) { return pc.getGroupId() + ":" + pc.getArtifactId(); //$NON-NLS-1$ } @Override public void setFocus() { super.setFocus(); txtName.setFocus(); } @Override public void init(IEditorSite site, IEditorInput input) { snippet = ((SnippetEditorInput) input).getSnippet(); registerEditorInputListener(); super.init(site, input); } private void registerEditorInputListener() { getEditor().addPropertyListener(new IPropertyListener() { @Override public void propertyChanged(Object source, int propId) { if (propId == PROP_INPUT) { setInputWithNotify(getEditor().getEditorInput()); } } }); } @Override protected void setInputWithNotify(IEditorInput input) { snippet = ((SnippetEditorInput) input).getSnippet(); context.dispose(); context = createDataBindingContext(); super.setInputWithNotify(input); } @Override public void dispose() { context.dispose(); super.dispose(); } /** * Show the snippet editor extensions discovery dialog. */ private void showDiscoveryDialog() { ICommandService cmdService = (ICommandService) getSite().getService(ICommandService.class); Command cmd = cmdService.getCommand(EXTENSION_DISCOVERY_COMMAND_ID); IHandlerService handlerService = (IHandlerService) getSite().getService(IHandlerService.class); try { ParameterizedCommand parmCommand = new ParameterizedCommand(cmd, new Parameterization[] { new Parameterization(cmd.getParameter(AbstractLinkContributionPage.COMMAND_HREF_ID), SNIPMATCH_P2_DISCOVERY_URL) }); handlerService.executeCommand(parmCommand, null); } catch (Exception e) { Logs.log(LogMessages.ERROR_FAILED_TO_EXECUTE_COMMAND, e, EXTENSION_DISCOVERY_COMMAND_ID); } } private static class NullSafeEnumToBooleanConverter extends EnumToBooleanConverter<Location> { private NullSafeEnumToBooleanConverter(Location[] trueValues) { super(trueValues); } @Override public Object convert(Object fromObject) { return fromObject != null ? super.convert(fromObject) : false; } } private static class FilenameRestrictionLabelProperty extends SimpleValueProperty { @Override public Object getValueType() { return String.class; } @Override protected Object doGetValue(Object source) { String text = (String) source; if (text.startsWith(".")) { return "*" + text; //$NON-NLS-1$ } else { return text; } } @Override protected void doSetValue(Object source, Object value) { } @Override public INativePropertyListener adaptListener(ISimplePropertyListener listener) { return null; } } private final class ObserveValueWithNullChecker extends ComputedValue { private final IObservableValue target; private final IObservableValue observableNullChecker; private final IObservableValue observableValue; private ObserveValueWithNullChecker(IObservableValue target, IObservableValue observableValue, IObservableValue observableNullChecker) { this.target = target; this.observableNullChecker = observableNullChecker; this.observableValue = observableValue; } @Override protected void doSetValue(Object value) { target.setValue(value); } @Override protected Object calculate() { boolean value = (boolean) observableValue.getValue(); return observableNullChecker.getValue() != null && value; } } }