com.ebmwebsourcing.petals.common.internal.provisional.emf.EObjectUIHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.ebmwebsourcing.petals.common.internal.provisional.emf.EObjectUIHelper.java

Source

/******************************************************************************
 * Copyright (c) 2011-2013, Linagora
 *
 * 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:
 *       Linagora - initial API and implementation
 *******************************************************************************/

package com.ebmwebsourcing.petals.common.internal.provisional.emf;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.databinding.EMFObservables;
import org.eclipse.emf.databinding.edit.EditingDomainEObjectObservableValue;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.AbstractOverrideableCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;

import com.ebmwebsourcing.petals.common.internal.Messages;
import com.ebmwebsourcing.petals.common.internal.provisional.utils.StringUtils;
import com.ebmwebsourcing.petals.common.internal.provisional.utils.SwtFactory;

/**
 * @author Mickael Istria - EBM WebSourcing
 */
public final class EObjectUIHelper {

    /**
     * Private constructor for utility class.
     */
    private EObjectUIHelper() {
        // nothing
    }

    /**
     * A validator for mandatory fields.
     */
    private static final class MandatoryFieldValidator implements IValidator {
        private final EStructuralFeature feature;

        /**
         * Constructor.
         * @param feature
         */
        public MandatoryFieldValidator(EStructuralFeature feature) {
            this.feature = feature;
        }

        /*
         * (non-Javadoc)
         * @see org.eclipse.core.databinding.validation.IValidator
         * #validate(java.lang.Object)
         */
        @Override
        public IStatus validate(Object value) {

            IStatus result = ValidationStatus.ok();
            if (value instanceof String && StringUtils.isEmpty((String) value)) {
                String label = StringUtils.camelCaseToHuman(this.feature.getName());
                label = StringUtils.capitalize(label);
                result = ValidationStatus.error(NLS.bind(Messages.fieldNotSet, label));
            }

            return result;
        }
    }

    /**
     * An entry description.
     */
    public static class EntryDescription {
        public Object widget;
        public EAttribute attribute;

        /**
         * Constructor.
         * @param widget
         * @param att
         */
        public EntryDescription(Object widget, EAttribute att) {
            this.widget = widget;
            this.attribute = att;
        }
    }

    /**
     * Generates a 2 column list with left column containing description of widgets and right column containing widget.
     * @param eObject the eObject to edit
     * @param toolkit a {@link FormToolkit} to create widgets
     * @param parent
     * @param domain the {@link EditingDomain} in case of transactional edition. Can be null, then no transaction is used.
     * @param dbc
     * @param toProcessFeatures list of features to edit.
     * @return
     */
    public static List<EntryDescription> generateWidgets(EObject eObject, FormToolkit toolkit, Composite parent,
            EditingDomain domain, DataBindingContext dbc, boolean showDecorator,
            EStructuralFeature... toProcessFeatures) {

        return generateWidgets(eObject, toolkit, null, parent, domain, dbc, showDecorator, toProcessFeatures);
    }

    /**
     * Generates a 2 column list with left column containing description of widgets and right column containing widget.
     * @param eObject the eObject to edit
     * @param toolkit a {@link FormToolkit} to create widgets
     * @param parent
     * @param domain the {@link EditingDomain} in case of transactional edition. Can be null, then no transaction is used.
     * @param dbc
     * @param toProcessFeatures list of features to edit.
     * @return
     */
    public static List<EntryDescription> generateEditorWidgets(EObject eObject, FormToolkit toolkit,
            Composite parent, EditingDomain domain, DataBindingContext dbc, boolean showDecorator,
            EStructuralFeature... toProcessFeatures) {

        return generateWidgets(eObject, toolkit, parent.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE), parent,
                domain, dbc, showDecorator, toProcessFeatures);
    }

    /**
     * Generates a 2 column list with left column containing description of widgets and right column containing widget.
     * @param eObject the eObject to edit
     * @param toolkit a {@link FormToolkit} to create widgets
     * @param labelColor the foreground color for the label
     * @param parent
     * @param domain the {@link EditingDomain} in case of transactional edition. Can be null, then no transaction is used.
     * @param dbc
     * @param toProcessFeatures list of features to edit.
     * @return
     */
    public static List<EntryDescription> generateWidgets(EObject eObject, FormToolkit toolkit, Color labelColor,
            Composite parent, EditingDomain domain, DataBindingContext dbc, boolean showDecorator,
            EStructuralFeature... toProcessFeatures) {

        Map<EStructuralFeature, String> map = new LinkedHashMap<EStructuralFeature, String>();
        for (EStructuralFeature feature : toProcessFeatures)
            map.put(feature, null);

        List<EntryDescription> entries = produceWidgets(toolkit, labelColor, parent, map);
        setUpDatabinding(eObject, domain, dbc, showDecorator, entries);
        return entries;
    }

    /**
     * Generates a 2 column list with left column containing description of widgets and right column containing widget.
     * @param eObject the eObject to edit
     * @param toolkit a {@link FormToolkit} to create widgets
     * @param labelColor the foreground color for the label
     * @param parent
     * @param domain the {@link EditingDomain} in case of transactional edition. Can be null, then no transaction is used.
     * @param dbc
     * @param toProcessFeatures list of features to edit.
     * @return
     */
    public static List<EntryDescription> generateWidget(EObject eObject, FormToolkit toolkit, Composite parent,
            EditingDomain domain, DataBindingContext dbc, boolean showDecorator, EStructuralFeature feature,
            String labelText) {

        Map<EStructuralFeature, String> map = new LinkedHashMap<EStructuralFeature, String>();
        map.put(feature, labelText);

        Color labelColor = parent.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE);
        List<EntryDescription> entries = produceWidgets(toolkit, labelColor, parent, map);
        setUpDatabinding(eObject, domain, dbc, showDecorator, entries);
        return entries;
    }

    /**
     * Sets up the data binding.
     * @param eObject
     * @param domain
     * @param dbc
     * @param entries
     */
    private static void setUpDatabinding(EObject eObject, EditingDomain domain, DataBindingContext dbc,
            boolean showDecorator, List<EntryDescription> entries) {

        for (EntryDescription entry : entries) {
            IObservableValue widgetObservable = null;
            if (entry.widget instanceof Text)
                widgetObservable = SWTObservables.observeDelayedValue(300,
                        SWTObservables.observeText((Text) entry.widget, SWT.Modify));

            else if (entry.widget instanceof StyledText)
                widgetObservable = SWTObservables.observeDelayedValue(300,
                        SWTObservables.observeText((StyledText) entry.widget, SWT.Modify));

            else if (entry.widget instanceof Spinner)
                widgetObservable = SWTObservables.observeSelection((Spinner) entry.widget);

            else if (entry.widget instanceof ISelectionProvider)
                widgetObservable = ViewersObservables.observeSingleSelection((ISelectionProvider) entry.widget);

            else if (entry.widget instanceof Button)
                widgetObservable = SWTObservables.observeSelection((Button) entry.widget);

            if (widgetObservable != null) {
                UpdateValueStrategy targetToModel = new UpdateValueStrategy();
                if (entry.attribute.getLowerBound() > 0)
                    targetToModel.setBeforeSetValidator(new MandatoryFieldValidator(entry.attribute));

                IObservableValue iov = domain == null ? EMFObservables.observeValue(eObject, entry.attribute)
                        // : EMFEditObservables.observeValue( domain, eObject, entry.attribute );
                        : createCustomEmfEditObservable(domain, eObject, entry.attribute);

                Binding binding;
                if (domain == null)
                    binding = dbc.bindValue(widgetObservable, iov, targetToModel, null);
                else
                    binding = dbc.bindValue(widgetObservable, iov);

                if (showDecorator && entry.attribute.getLowerBound() > 0)
                    ControlDecorationSupport.create(binding, SWT.TOP | SWT.LEFT);
            }
        }
    }

    /**
     * Creates an observable using an editing domain.
     * <p>
     * This method is a workaround for SetCommands (EMF version <2.7.1).
     * Keep it, even if it is not used.
     * </p>
     *
     * @param domain
     * @param eo
     * @param ea
     * @return an IObservable value
     * TODO: replace this method by EMFEditObservables as soon as EMF 2.8.0 or 2.7.2 is out
     * @See https://bugs.eclipse.org/bugs/show_bug.cgi?id=356291
     * @See https://bugs.eclipse.org/bugs/show_bug.cgi?id=359043
     */
    public static IObservableValue createCustomEmfEditObservable(EditingDomain domain, final EObject eo,
            final EAttribute ea) {

        return new EditingDomainEObjectObservableValue(domain, eo, ea) {
            @Override
            protected void doSetValue(final Object value) {
                Command command = createCustomSetCommand(this.domain, eo, ea, value);
                this.domain.getCommandStack().execute(command);
            }
        };
    }

    /**
     * Creates a custom set command to use with model extensions.
     * @param domain
     * @param eo
     * @param ea
     * @param value
     * @return a custom set command (with less checks)
     * TODO: replace this method by the real SetCommand as soon as EMF 2.8.0 or 2.7.2 is out
     * @See https://bugs.eclipse.org/bugs/show_bug.cgi?id=356291
     * @See https://bugs.eclipse.org/bugs/show_bug.cgi?id=359043
     */
    public static Command createCustomSetCommand(final EditingDomain domain, final EObject eo, final EAttribute ea,
            final Object value) {

        return new AbstractOverrideableCommand(domain, "MySetCommand") {
            private Object oldValue;

            @Override
            public void doExecute() {
                this.oldValue = eo.eGet(ea);
                eo.eSet(ea, value);
            }

            @Override
            public void doUndo() {
                eo.eSet(ea, this.oldValue);
            }

            @Override
            public void doRedo() {
                execute();
            }

            @Override
            public boolean doCanExecute() {
                return true;
            }
        };
    }

    /**
     * Produces the widgets.
     * @param toolkit
     * @param parent
     * @param featuresToLabels
     * @return
     */
    private static List<EntryDescription> produceWidgets(FormToolkit toolkit, Color labelColor, Composite parent,
            Map<EStructuralFeature, String> featuresToLabels) {

        List<EntryDescription> entries = new ArrayList<EntryDescription>();
        for (Map.Entry<EStructuralFeature, String> entry : featuresToLabels.entrySet()) {
            Object widget = null;
            EAttribute attr = (EAttribute) entry.getKey();
            String label = entry.getValue();

            // The label
            // TODO leverage ExtendedMetaData.INSTANCE for tool tip and label
            if (label == null) {
                label = StringUtils.camelCaseToHuman(attr.getName());
                label = StringUtils.capitalize(label);
                if (attr.getLowerBound() > 0)
                    label += " *";
                label += ":";
            }

            Label labelWidget = toolkit.createLabel(parent, label);
            labelWidget.setBackground(parent.getBackground());
            if (labelColor != null)
                labelWidget.setForeground(labelColor);

            // The widget
            Class<?> instanceClass = attr.getEType().getInstanceClass();
            if (instanceClass.equals(String.class)) {

                String lowered = label.toLowerCase();
                if (lowered.contains("password") || lowered.contains("passphrase"))
                    widget = SwtFactory.createPasswordField(parent, false).getText();

                else if (lowered.contains("folder") || lowered.contains("directory"))
                    widget = SwtFactory.createDirectoryBrowser(parent).getText();

                else
                    widget = toolkit.createText(parent, "", SWT.BORDER);

                ((Text) widget).setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

            } else if (instanceClass.equals(Integer.class) || instanceClass.equals(int.class)) {
                widget = new Spinner(parent, SWT.BORDER);
                GridDataFactory.swtDefaults().hint(100, SWT.DEFAULT).minSize(100, SWT.DEFAULT)
                        .applyTo((Spinner) widget);
                ((Spinner) widget).setMaximum(Integer.MAX_VALUE);

            } else if (instanceClass.isEnum()) {
                widget = new ComboViewer(parent, SWT.READ_ONLY | SWT.FLAT);
                ComboViewer viewer = (ComboViewer) widget;
                viewer.setContentProvider(new EEnumLiteralsProvider());
                viewer.setLabelProvider(new EEnumNameLabelProvider());
                viewer.setInput(attr.getEType());
                viewer.getCombo().setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

            } else if (instanceClass.equals(Boolean.class) || instanceClass.equals(boolean.class)) {
                widget = new ComboViewer(parent, SWT.READ_ONLY | SWT.FLAT);
                ComboViewer viewer = (ComboViewer) widget;
                viewer.setContentProvider(new ArrayContentProvider());
                viewer.setLabelProvider(new LabelProvider());
                viewer.setInput(new Boolean[] { Boolean.TRUE, Boolean.FALSE });

                Combo combo = ((ComboViewer) widget).getCombo();
                GridDataFactory.swtDefaults().hint(100, SWT.DEFAULT).minSize(100, SWT.DEFAULT).applyTo(combo);
            }

            if (widget != null)
                entries.add(new EntryDescription(widget, attr));
        }

        return entries;
    }
}