org.bonitasoft.studio.expression.editor.viewer.ExpressionViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.bonitasoft.studio.expression.editor.viewer.ExpressionViewer.java

Source

/**
 * Copyright (C) 2012 BonitaSoft S.A.
 * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2.0 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.bonitasoft.studio.expression.editor.viewer;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import org.bonitasoft.studio.common.AbstractRefactorOperation;
import org.bonitasoft.studio.common.ExpressionConstants;
import org.bonitasoft.studio.common.IBonitaVariableContext;
import org.bonitasoft.studio.common.extension.BonitaStudioExtensionRegistryManager;
import org.bonitasoft.studio.common.jface.SWTBotConstants;
import org.bonitasoft.studio.common.jface.databinding.validator.EmptyInputValidator;
import org.bonitasoft.studio.common.log.BonitaStudioLog;
import org.bonitasoft.studio.expression.editor.ExpressionEditorPlugin;
import org.bonitasoft.studio.expression.editor.autocompletion.AutoCompletionField;
import org.bonitasoft.studio.expression.editor.autocompletion.BonitaContentProposalAdapter;
import org.bonitasoft.studio.expression.editor.autocompletion.ExpressionProposal;
import org.bonitasoft.studio.expression.editor.autocompletion.IBonitaContentProposalListener2;
import org.bonitasoft.studio.expression.editor.autocompletion.IExpressionProposalLabelProvider;
import org.bonitasoft.studio.expression.editor.i18n.Messages;
import org.bonitasoft.studio.expression.editor.provider.ExpressionComparator;
import org.bonitasoft.studio.expression.editor.provider.ExpressionContentProvider;
import org.bonitasoft.studio.expression.editor.provider.ExpressionLabelProvider;
import org.bonitasoft.studio.expression.editor.provider.ExpressionTypeLabelProvider;
import org.bonitasoft.studio.expression.editor.provider.IExpressionNatureProvider;
import org.bonitasoft.studio.expression.editor.provider.IExpressionToolbarContribution;
import org.bonitasoft.studio.expression.editor.provider.IExpressionValidator;
import org.bonitasoft.studio.expression.editor.widget.ContentAssistText;
import org.bonitasoft.studio.model.expression.Expression;
import org.bonitasoft.studio.model.expression.ExpressionFactory;
import org.bonitasoft.studio.model.expression.ExpressionPackage;
import org.bonitasoft.studio.model.form.Duplicable;
import org.bonitasoft.studio.model.form.TextFormField;
import org.bonitasoft.studio.model.form.Widget;
import org.bonitasoft.studio.pics.Pics;
import org.bonitasoft.studio.pics.PicsConstants;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.conversion.Converter;
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.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.EMFProperties;
import org.eclipse.emf.databinding.edit.EMFEditProperties;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.emf.workspace.WorkspaceEditingDomainFactory;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ViewerProperties;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalListener;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.DefaultToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;

/**
 * @author Romain Bioteau
 * 
 */
public class ExpressionViewer extends ContentViewer implements ExpressionConstants, SWTBotConstants,
        IContentProposalListener, IBonitaContentProposalListener2, IBonitaVariableContext {

    protected Composite control;
    private Text textControl;
    protected ToolItem editControl;
    private AutoCompletionField autoCompletion;
    protected EMFDataBindingContext internalDataBindingContext;
    protected EditingDomain editingDomain;
    protected Expression selectedExpression;
    protected final Set<ViewerFilter> filters;
    private String example;
    private ControlDecoration messageDecoration;
    private boolean disposeDomain = false;
    protected String mandatoryFieldName;
    private ControlDecoration typeDecoration;
    private boolean editing = false;
    private EObject context;
    private final List<ISelectionChangedListener> expressionEditorListener = new ArrayList<ISelectionChangedListener>();
    private boolean withConnector = false;
    private List<IExpressionValidationListener> validationListeners = new ArrayList<IExpressionValidationListener>();
    private ToolItem eraseControl;
    private boolean isPageFlowContext = false;
    private AbstractRefactorOperation operation;
    private AbstractRefactorOperation removeOperation;

    protected final DisposeListener disposeListener = new DisposeListener() {

        @Override
        public void widgetDisposed(DisposeEvent e) {
            handleDispose(e);
        }
    };
    private final EReference expressionReference;
    protected IExpressionNatureProvider expressionNatureProvider = new ExpressionContentProvider();
    protected DataBindingContext externalDataBindingContext;
    protected Binding expressionBinding;
    private final Map<Integer, String> messages = new HashMap<Integer, String>();
    private ToolBar toolbar;
    private List<IExpressionToolbarContribution> toolbarContributions = new ArrayList<IExpressionToolbarContribution>();
    private Map<String, IExpressionValidator> validatorsForType = new HashMap<String, IExpressionValidator>();
    protected boolean isPassword;
    private DefaultToolTip textTooltip;
    private IExpressionProposalLabelProvider expressionProposalLableProvider;
    private ContentAssistText contentAssistText;

    public ExpressionViewer(Composite composite, int style, EReference expressionReference) {
        this(composite, style, null, null, expressionReference);
    }

    public ExpressionViewer(Composite composite, int style, TabbedPropertySheetWidgetFactory widgetFactory,
            EReference expressionReference) {
        this(composite, style, widgetFactory, null, expressionReference);
    }

    public ExpressionViewer(Composite composite, int style, TabbedPropertySheetWidgetFactory widgetFactory,
            EditingDomain editingDomain, EReference expressionReference) {
        this(composite, style, widgetFactory, editingDomain, expressionReference, false);

    }

    public ExpressionViewer(Composite composite, int style, TabbedPropertySheetWidgetFactory widgetFactory,
            EditingDomain editingDomain, EReference expressionReference, boolean withConnector) {
        this.editingDomain = editingDomain;
        this.expressionReference = expressionReference;
        filters = new HashSet<ViewerFilter>();
        this.withConnector = withConnector;
        createControl(composite, style, widgetFactory);
        setContentProvider(new ArrayContentProvider());
        setLabelProvider(new ExpressionLabelProvider());
    }

    protected void createControl(Composite composite, int style, TabbedPropertySheetWidgetFactory widgetFactory) {
        if (widgetFactory != null) {
            control = widgetFactory.createComposite(composite, SWT.INHERIT_DEFAULT);
        } else {
            control = new Composite(composite, SWT.INHERIT_DEFAULT);
        }
        control.addDisposeListener(disposeListener);
        control.setLayout(GridLayoutFactory.fillDefaults().numColumns(2).margins(0, 0).spacing(0, 0).create());
        createTextControl(style, widgetFactory);
        createToolbar(style, widgetFactory);

    }

    protected void createToolbar(int style, TabbedPropertySheetWidgetFactory widgetFactory) {
        toolbar = new ToolBar(control, SWT.FLAT | SWT.NO_FOCUS);
        toolbar.setLayoutData(
                GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(false, false).create());
        editControl = createEditToolItem(toolbar);
        if (withConnector) {
            createToolBarExtension(toolbar);
        }
        createEraseToolItem(toolbar);
        if (widgetFactory != null) {
            widgetFactory.adapt(toolbar, true, true);
        }
    }

    protected void createToolBarExtension(ToolBar toolbar) {
        for (IConfigurationElement elem : BonitaStudioExtensionRegistryManager.getInstance()
                .getConfigurationElements("org.bonitasoft.studio.expression.editor.expressionViewerToolbar")) {
            try {
                final IExpressionToolbarContribution item = (IExpressionToolbarContribution) elem
                        .createExecutableExtension("class");
                if (item.getId() == "ConnectorInExpressionViewer" && withConnector) {
                    item.fill(toolbar, -1);
                    item.setExpressionViewer(this);
                }
                toolbarContributions.add(item);
            } catch (CoreException e) {
                BonitaStudioLog.error(e, ExpressionEditorPlugin.PLUGIN_ID);
            }
        }
    }

    protected ToolItem createEditToolItem(final ToolBar tb) {
        final ToolItem editControl = new ToolItem(tb, SWT.PUSH | SWT.NO_FOCUS);
        editControl.setImage(Pics.getImage(PicsConstants.edit));
        editControl.setToolTipText(Messages.editAndContinue);

        /* For test purpose */
        editControl.setData(SWTBOT_WIDGET_ID_KEY, SWTBOT_ID_EDITBUTTON);
        editControl.addListener(SWT.Selection, new Listener() {
            @Override
            public void handleEvent(Event event) {
                boolean connectorEdit = false;
                if (tb != null && withConnector && selectedExpression != null
                        && ExpressionConstants.CONNECTOR_TYPE.equals(selectedExpression.getType())) {
                    for (ToolItem ti : tb.getItems()) {
                        Object data = ti.getData(SWTBotConstants.SWTBOT_WIDGET_ID_KEY);
                        if (data != null && data.equals(SWTBotConstants.SWTBOT_ID_CONNECTORBUTTON)) {
                            connectorEdit = true;
                            ti.notifyListeners(SWT.Selection, event);
                        }
                    }
                }
                if (!connectorEdit) {
                    openEditDialog();
                }
            }
        });

        editControl.addDisposeListener(disposeListener);
        return editControl;
    }

    protected ToolItem createEraseToolItem(ToolBar tb) {
        eraseControl = new ToolItem(tb, SWT.PUSH | SWT.NO_FOCUS);
        eraseControl.setImage(Pics.getImage(PicsConstants.clear));
        eraseControl.setToolTipText(Messages.eraseExpression);

        /* For test purpose */
        eraseControl.setData(SWTBOT_WIDGET_ID_KEY, SWTBOT_ID_ERASEBUTTON);
        eraseControl.addListener(SWT.Selection, new Listener() {
            @Override
            public void handleEvent(Event event) {
                String type = selectedExpression.getType();
                if (ExpressionConstants.SCRIPT_TYPE.equals(type)) {
                    if (!MessageDialog.openConfirm(Display.getDefault().getActiveShell(),
                            Messages.cleanExpressionTitle, Messages.cleanExpressionMsg)) {
                        return;
                    }
                }
                if (!ExpressionConstants.CONDITION_TYPE.equals(type)) {
                    type = ExpressionConstants.CONSTANT_TYPE;
                }
                if (editingDomain != null) {

                    CompoundCommand cc = new CompoundCommand();
                    cc.append(SetCommand.create(editingDomain, selectedExpression,
                            ExpressionPackage.Literals.EXPRESSION__TYPE, type));
                    cc.append(SetCommand.create(editingDomain, selectedExpression,
                            ExpressionPackage.Literals.EXPRESSION__NAME, ""));
                    cc.append(SetCommand.create(editingDomain, selectedExpression,
                            ExpressionPackage.Literals.EXPRESSION__CONTENT, ""));
                    cc.append(RemoveCommand.create(editingDomain, selectedExpression,
                            ExpressionPackage.Literals.EXPRESSION__REFERENCED_ELEMENTS,
                            selectedExpression.getReferencedElements()));
                    cc.append(RemoveCommand.create(editingDomain, selectedExpression,
                            ExpressionPackage.Literals.EXPRESSION__CONNECTORS, selectedExpression.getConnectors()));
                    boolean hasBeenExecuted = executeRemoveOperation(cc);
                    if (!hasBeenExecuted) {
                        editingDomain.getCommandStack().execute(cc);
                    }
                } else {
                    selectedExpression.setType(type);
                    selectedExpression.setName("");
                    selectedExpression.setContent("");
                    selectedExpression.getReferencedElements().clear();
                    selectedExpression.getConnectors().clear();
                }

                textControl.setText("");
                validate();
                refresh();
            }
        });

        eraseControl.addDisposeListener(disposeListener);
        return eraseControl;
    }

    public void setExpressionProposalLableProvider(
            IExpressionProposalLabelProvider expressionProposalLableProvider) {
        this.expressionProposalLableProvider = expressionProposalLableProvider;
        if (autoCompletion != null) {
            autoCompletion.setExpressionProposalLabelProvider(expressionProposalLableProvider);
        }
    }

    public ContentAssistText getContentAssistText() {
        return contentAssistText;
    }

    protected void createTextControl(int style, TabbedPropertySheetWidgetFactory widgetFactory) {
        if (expressionProposalLableProvider == null) {
            expressionProposalLableProvider = new ExpressionLabelProvider();
        }
        contentAssistText = new ContentAssistText(control, expressionProposalLableProvider, style);
        textControl = contentAssistText.getTextControl();
        if (widgetFactory != null) {
            widgetFactory.adapt(textControl, false, false);
        }
        textControl.addDisposeListener(disposeListener);
        textTooltip = new DefaultToolTip(textControl) {
            @Override
            protected boolean shouldCreateToolTip(Event event) {
                return super.shouldCreateToolTip(event) && getText(event) != null;
            }
        };
        textTooltip.setShift(new Point(5, 5));
        textTooltip.setRespectMonitorBounds(true);
        textTooltip.setPopupDelay(100);

        typeDecoration = new ControlDecoration(contentAssistText.getToolbar(), SWT.LEFT, control);
        typeDecoration.setMarginWidth(0);

        messageDecoration = new ControlDecoration(contentAssistText, SWT.LEFT, control);
        messageDecoration.setShowHover(true);
        messageDecoration.setMarginWidth(1);
        messageDecoration.hide();

        contentAssistText.addContentAssistListener(this);
        autoCompletion = contentAssistText.getAutocompletion();
        autoCompletion.addExpressionProposalListener(this);

        int indent = 0;
        if ((style & SWT.PASSWORD) != 0) {
            isPassword = true;
        }
        if ((style & SWT.BORDER) != 0) {
            indent = 16;
        }
        contentAssistText.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).indent(indent, 0)
                .grab(true, false).create());
    }

    protected void openEditDialog() {
        final Object input = getInput();
        EObject editInput = context;
        if (input != null) {
            if (editInput == null) {
                if (input instanceof EObject) {
                    editInput = (EObject) input;
                } else {
                    editInput = (EObject) ((IObservableValue) input).getValue();
                }
            }
        }
        EditExpressionDialog dialog = createEditDialog(editInput);
        dialog.setIsPageFlowContext(isPageFlowContext);
        if (dialog.open() == Dialog.OK) {
            Expression newExpression = dialog.getExpression();
            executeOperation(newExpression.getName());
            updateSelection(newExpression);
            setSelection(new StructuredSelection(selectedExpression));
            if (editingDomain == null) {

                selectedExpression.setReturnType(newExpression.getReturnType());
                selectedExpression.setType(newExpression.getType());
            } else {
                editingDomain.getCommandStack().execute(SetCommand.create(editingDomain, selectedExpression,
                        ExpressionPackage.Literals.EXPRESSION__RETURN_TYPE, newExpression.getReturnType()));
                editingDomain.getCommandStack().execute(SetCommand.create(editingDomain, selectedExpression,
                        ExpressionPackage.Literals.EXPRESSION__TYPE, newExpression.getType()));
            }

            refresh();
            fireExpressionEditorChanged(
                    new SelectionChangedEvent(this, new StructuredSelection(selectedExpression)));
        }
    }

    protected EditExpressionDialog createEditDialog(EObject editInput) {
        return new EditExpressionDialog(control.getShell(), isPassword, EcoreUtil.copy(selectedExpression),
                editInput, editingDomain, filters.toArray(new ViewerFilter[filters.size()]), this);
    }

    protected void fireExpressionEditorChanged(SelectionChangedEvent selectionChangedEvent) {
        for (ISelectionChangedListener listener : expressionEditorListener) {
            listener.selectionChanged(selectionChangedEvent);
        }

    }

    @Override
    public Control getControl() {
        return control;
    }

    public ToolBar getToolbar() {
        return toolbar;
    }

    @Override
    public ISelection getSelection() {
        if (selectedExpression != null) {
            return new StructuredSelection(selectedExpression);
        } else {
            return new StructuredSelection();
        }
    }

    public BonitaContentProposalAdapter getContentProposal() {
        return autoCompletion.getContentProposalAdapter();
    }

    @Override
    public void refresh() {
        if (selectedExpression != null) {
            internalRefresh(selectedExpression);
        }
    }

    private Image getLabelProviderImage(ILabelProvider labelProvider, Object input) {
        return labelProvider.getImage(input);
    }

    @Override
    public void setSelection(ISelection selection, boolean reveal) {

        if (selection instanceof IStructuredSelection) {
            Object sel = ((IStructuredSelection) selection).getFirstElement();
            if (sel instanceof Expression) {
                selectedExpression = (Expression) sel;
                bindExpression();
                if (editingDomain == null) {
                    editingDomain = TransactionUtil.getEditingDomain(selectedExpression);
                }
                final StructuredSelection selection2 = new StructuredSelection(selectedExpression);
                fireSelectionChanged(new SelectionChangedEvent(this, selection2));
                for (IExpressionToolbarContribution contribution : toolbarContributions) {
                    contribution.setExpression(selectedExpression);
                }
                if (ExpressionConstants.CONDITION_TYPE.equals(selectedExpression.getType())) {
                    autoCompletion.getContentProposalAdapter().setEnabled(false);
                    autoCompletion.getContentProposalAdapter()
                            .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_INSERT);
                } else {
                    autoCompletion.getContentProposalAdapter().setEnabled(true);
                    autoCompletion.getContentProposalAdapter()
                            .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
                }
                validate();
                refresh();
            } else {
                selectedExpression = null;
            }
        }
        editing = false;

    }

    protected void updateSelection(Expression expression) {
        new ExpressionSynchronizer(editingDomain, expression, selectedExpression).synchronize();
        refresh();
    }

    @Override
    protected void inputChanged(Object input, Object oldInput) {
        if (input instanceof IObservableValue) {
            input = ((IObservableValue) input).getValue();
        }
        if (input != null && input instanceof EObject && expressionReference != null) {
            selectedExpression = (Expression) ((EObject) input).eGet(expressionReference);
        } else {
            // FIXME: it might be
            // org.eclipse.emf.ecore.util.EObjectContainmentEList
        }
        if (editingDomain == null) {
            if (input != null && input instanceof EObject) {
                editingDomain = TransactionUtil.getEditingDomain(input);
            }
        }
        manageNatureProviderAndAutocompletionProposal(input);
    }

    public void manageNatureProviderAndAutocompletionProposal(Object input) {
        if (expressionNatureProvider != null) {
            if (context == null) {
                expressionNatureProvider.setContext((EObject) input);
            } else {
                expressionNatureProvider.setContext(context);
            }
        }
        if (selectedExpression != null && ExpressionConstants.CONDITION_TYPE.equals(selectedExpression.getType())) {
            setProposalsFiltering(false);
            autoCompletion.getContentProposalAdapter()
                    .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_INSERT);
        } else {
            autoCompletion.getContentProposalAdapter()
                    .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
        }
        autoCompletion.setContext(expressionNatureProvider.getContext());
        final Set<Expression> filteredExpressions = getFilteredExpressions();
        autoCompletion.setProposals(filteredExpressions.toArray(new Expression[filteredExpressions.size()]));

        final ArrayList<String> filteredExpressionType = getFilteredExpressionType();
        autoCompletion.setFilteredExpressionType(filteredExpressionType);
        if ((filteredExpressionType.contains(ExpressionConstants.VARIABLE_TYPE)
                && filteredExpressionType.contains(ExpressionConstants.PARAMETER_TYPE)
                && filteredExpressions.isEmpty())) {
            contentAssistText.setProposalEnabled(false);
        } else {
            contentAssistText.setProposalEnabled(true);
        }
    }

    private ArrayList<String> getFilteredExpressionType() {
        final ArrayList<String> filteredExpressions = new ArrayList<String>();
        final Set<ViewerFilter> fitlers = getFilters();
        final EObject input = expressionNatureProvider.getContext();

        Expression exp = ExpressionFactory.eINSTANCE.createExpression();
        exp.setName("");
        if (filters != null && expressionNatureProvider != null && input != null) {
            for (ViewerFilter viewerFilter : fitlers) {
                exp.setType(ExpressionConstants.VARIABLE_TYPE);
                if (!viewerFilter.select(this, input, exp)) {
                    filteredExpressions.add(ExpressionConstants.VARIABLE_TYPE);
                }
                exp.setType(ExpressionConstants.PARAMETER_TYPE);
                if (!viewerFilter.select(this, input, exp)) {
                    filteredExpressions.add(ExpressionConstants.PARAMETER_TYPE);
                }
                exp.setType(ExpressionConstants.CONSTANT_TYPE);
                if (!viewerFilter.select(this, input, exp)) {
                    filteredExpressions.add(ExpressionConstants.CONSTANT_TYPE);
                }
                exp.setType(ExpressionConstants.DOCUMENT_TYPE);
                if (!viewerFilter.select(this, input, exp)) {
                    filteredExpressions.add(ExpressionConstants.DOCUMENT_TYPE);
                }
                exp.setType(ExpressionConstants.DOCUMENT_REF_TYPE);
                if (!viewerFilter.select(this, input, exp)) {
                    filteredExpressions.add(ExpressionConstants.DOCUMENT_REF_TYPE);
                }
            }
        }
        return filteredExpressions;
    }

    public void setExpressionNatureProvider(IExpressionNatureProvider expressionNatureProvider) {
        this.expressionNatureProvider = expressionNatureProvider;
    }

    protected Set<Expression> getFilteredExpressions() {
        Set<Expression> filteredExpressions = new TreeSet<Expression>(new ExpressionComparator());
        if (expressionNatureProvider != null) {
            if (!(expressionNatureProvider instanceof ExpressionContentProvider)) {
                final ExpressionContentProvider provider = new ExpressionContentProvider();
                if (context == null) {
                    provider.setContext((EObject) getInput());
                } else {
                    provider.setContext(context);
                }
                Expression[] expressions = provider.getExpressions();
                if (expressions != null) {
                    filteredExpressions.addAll(Arrays.asList(expressions));
                }
            }
            Expression[] expressions = expressionNatureProvider.getExpressions();
            EObject input = expressionNatureProvider.getContext();
            if (input == null) {
                if (getInput() instanceof EObject) {
                    input = (EObject) getInput();
                }
            }
            if (expressions != null) {
                filteredExpressions.addAll(Arrays.asList(expressions));
            }
            Set<Expression> toRemove = new HashSet<Expression>();
            if (input != null) {
                for (Expression exp : filteredExpressions) {
                    for (ViewerFilter filter : getFilters()) {
                        if (filter != null && !filter.select(this, input, exp)) {
                            toRemove.add(exp);
                        }
                    }
                    if (selectedExpression != null
                            && !ExpressionConstants.CONDITION_TYPE.equals(selectedExpression.getType())) {
                        if (selectedExpression != null && selectedExpression.isReturnTypeFixed()
                                && selectedExpression.getReturnType() != null) {
                            if (!compatibleReturnTypes(selectedExpression, exp)) {
                                toRemove.add(exp);
                            }
                        }
                    }
                }
            }
            filteredExpressions.removeAll(toRemove);
        }
        return filteredExpressions;
    }

    protected boolean compatibleReturnTypes(Expression currentExpression, Expression targetExpression) {
        final String currentReturnType = currentExpression.getReturnType();
        final String targetReturnType = targetExpression.getReturnType();
        if (currentReturnType.equals(targetReturnType)) {
            return true;
        }
        try {
            Class<?> currentReturnTypeClass = Class.forName(currentReturnType);
            Class<?> targetReturnTypeClass = Class.forName(targetReturnType);
            return currentReturnTypeClass.isAssignableFrom(targetReturnTypeClass);
        } catch (ClassNotFoundException e) {
            BonitaStudioLog.debug("Failed to determine the compatibility between " + targetReturnType + " and "
                    + currentReturnType, ExpressionEditorPlugin.PLUGIN_ID);
        }
        return true;
    }

    protected Set<ViewerFilter> getFilters() {
        return filters;
    }

    protected void bindExpression() {
        if (expressionBinding != null && externalDataBindingContext != null) {
            externalDataBindingContext.removeBinding(expressionBinding);
            expressionBinding.dispose();
        }
        if (internalDataBindingContext != null) {
            internalDataBindingContext.dispose();
        }
        internalDataBindingContext = new EMFDataBindingContext();

        IObservableValue nameObservable = null;
        if (editingDomain != null) {
            nameObservable = EMFEditProperties.value(editingDomain, ExpressionPackage.Literals.EXPRESSION__NAME)
                    .observeDetail(ViewerProperties.singleSelection().observe(this));
        } else {
            nameObservable = EMFProperties.value(ExpressionPackage.Literals.EXPRESSION__NAME)
                    .observeDetail(ViewerProperties.singleSelection().observe(this));
        }

        IObservableValue typeObservable = null;
        if (editingDomain != null) {
            typeObservable = EMFEditProperties.value(editingDomain, ExpressionPackage.Literals.EXPRESSION__TYPE)
                    .observeDetail(ViewerProperties.singleSelection().observe(this));
        } else {
            typeObservable = EMFProperties.value(ExpressionPackage.Literals.EXPRESSION__TYPE)
                    .observeDetail(ViewerProperties.singleSelection().observe(this));
        }

        UpdateValueStrategy targetToModelNameStrategy = new UpdateValueStrategy();
        if (mandatoryFieldName != null) {
            targetToModelNameStrategy.setBeforeSetValidator(new EmptyInputValidator(mandatoryFieldName));
        }
        targetToModelNameStrategy.setAfterConvertValidator(new IValidator() {

            @Override
            public IStatus validate(Object value) {
                IExpressionValidator validator = null;
                if (selectedExpression != null) {
                    validator = validatorsForType.get(selectedExpression.getType());
                }
                if (validator == null) {
                    validator = validatorsForType.get(ExpressionConstants.ALL_TYPES);
                }
                if (validator != null) {
                    validator.setDomain(editingDomain);
                    validator.setContext(context);
                    if (selectedExpression != null) {
                        validator.setInputExpression(selectedExpression);
                    }
                    setMessage(null, IStatus.OK);
                    final IStatus status = validator.validate(value);
                    if (status.isOK()) {
                        setMessage(null, status.getSeverity());
                    } else {
                        String message = status.getMessage();
                        if (status instanceof MultiStatus) {
                            StringBuilder sb = new StringBuilder();
                            for (IStatus statusChild : status.getChildren()) {
                                sb.append(statusChild.getMessage());
                                sb.append("\n");
                            }
                            if (sb.length() > 0) {
                                sb.delete(sb.length() - 1, sb.length());
                            }
                            message = sb.toString();
                        }
                        setMessage(message, status.getSeverity());
                    }
                    return status;
                } else {
                    setMessage(null, IStatus.OK);
                }
                return ValidationStatus.ok();
            }
        });
        targetToModelNameStrategy.setConverter(getNameConverter());

        final ISWTObservableValue observeDelayedValue = SWTObservables.observeDelayedValue(500,
                SWTObservables.observeText(textControl, SWT.Modify));
        expressionBinding = internalDataBindingContext.bindValue(observeDelayedValue, nameObservable,
                targetToModelNameStrategy, null);

        expressionBinding.getValidationStatus().addValueChangeListener(new IValueChangeListener() {

            @Override
            public void handleValueChange(ValueChangeEvent event) {
                IStatus status = (IStatus) event.diff.getNewValue();
                fireValidationStatusChanged(status.getSeverity());
            }
        });

        bindEditableText(typeObservable);
        nameObservable.addValueChangeListener(new IValueChangeListener() {

            @Override
            public void handleValueChange(ValueChangeEvent arg0) {
                fireSelectionChanged(new SelectionChangedEvent(ExpressionViewer.this, getSelection()));
            }
        });
        if (externalDataBindingContext != null) {
            externalDataBindingContext.addBinding(expressionBinding);
        }
    }

    protected void bindEditableText(IObservableValue typeObservable) {
        UpdateValueStrategy modelToTargetTypeStrategy = new UpdateValueStrategy();
        modelToTargetTypeStrategy.setConverter(new Converter(String.class, Boolean.class) {

            @Override
            public Object convert(Object from) {
                final boolean isScriptType = ExpressionConstants.SCRIPT_TYPE.equals(from.toString());
                final boolean isConnectorType = from.toString().equals(ExpressionConstants.CONNECTOR_TYPE);
                final boolean isXPathType = from.toString().equals(ExpressionConstants.XPATH_TYPE);
                final boolean isJavaType = from.toString().equals(ExpressionConstants.JAVA_TYPE);
                if (isScriptType) {
                    textTooltip.setText(Messages.editScriptExpressionTooltip);
                } else if (isXPathType) {
                    textTooltip.setText(Messages.editXpathExpressionTooltip);
                } else if (isJavaType) {
                    textTooltip.setText(Messages.editJavaExpressionTooltip);
                } else if (isConnectorType) {
                    textTooltip.setText(Messages.editConnectorExpressionTooltip);
                } else {
                    textTooltip.setText(null);
                }
                return !(isScriptType || isConnectorType || isJavaType || isXPathType);
            }

        });

        internalDataBindingContext.bindValue(SWTObservables.observeEditable(textControl), typeObservable,
                new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), modelToTargetTypeStrategy);
    }

    protected void updateContent(String newContent) {
        if (newContent != null && !newContent.equals(selectedExpression.getContent())) {
            if (editingDomain != null) {
                editingDomain.getCommandStack().execute(SetCommand.create(editingDomain, selectedExpression,
                        ExpressionPackage.Literals.EXPRESSION__CONTENT, newContent));
            } else {
                selectedExpression.setContent(newContent);
            }
        }
    }

    protected void updateContentType(String newContentType) {
        if (!newContentType.equals(selectedExpression.getType())) {
            if (editingDomain != null) {
                editingDomain.getCommandStack().execute(SetCommand.create(editingDomain, selectedExpression,
                        ExpressionPackage.Literals.EXPRESSION__TYPE, newContentType));
            } else {
                selectedExpression.setType(newContentType);
            }
            updateInterpreter(newContentType);
        }
    }

    private void updateInterpreter(String newContentType) {
        if (!ExpressionConstants.SCRIPT_TYPE.equals(newContentType)) {
            if (editingDomain != null) {
                editingDomain.getCommandStack().execute(SetCommand.create(editingDomain, selectedExpression,
                        ExpressionPackage.Literals.EXPRESSION__INTERPRETER, null));
            } else {
                selectedExpression.setInterpreter(null);
            }
        }
    }

    protected String getContentFromInput(String input) {
        final String selectedExpressionType = selectedExpression.getType();
        if (ExpressionConstants.SCRIPT_TYPE.equals(selectedExpressionType)
                || ExpressionConstants.PATTERN_TYPE.equals(selectedExpressionType)
                || ExpressionConstants.XPATH_TYPE.equals(selectedExpressionType)
                || ExpressionConstants.JAVA_TYPE.equals(selectedExpressionType)) {
            return selectedExpression.getContent(); // NO CONTENT UPDATE WHEN
            // THOSES TYPES
        }

        Set<String> cache = new HashSet<String>();
        for (Expression e : getFilteredExpressions()) {
            if (e.getName().equals(input)) {
                cache.add(e.getContent());
            }
        }
        if (cache.size() > 1) {
            for (String content : cache) {
                if (content.equals(selectedExpression.getContent())) {
                    return content;
                }
            }
            return cache.iterator().next();
        } else if (cache.size() == 1) {
            return cache.iterator().next();
        }
        return input;
    }

    protected String getContentTypeFromInput(String input) {
        Assert.isNotNull(selectedExpression);
        String expressionType = selectedExpression.getType();
        if (CONSTANT_TYPE.equals(expressionType)) {
            return expressionType;
        }
        if (selectedExpression.getType() == null) {
            expressionType = CONSTANT_TYPE;
        }
        if (ExpressionConstants.SCRIPT_TYPE.equals(expressionType)) {
            return ExpressionConstants.SCRIPT_TYPE;
        } else if (ExpressionConstants.CONDITION_TYPE.equals(expressionType)) {
            return ExpressionConstants.CONDITION_TYPE;
        } else if (ExpressionConstants.CONNECTOR_TYPE.equals(expressionType)) {
            return ExpressionConstants.CONNECTOR_TYPE;
        } else if (ExpressionConstants.PATTERN_TYPE.equals(expressionType)) {
            return ExpressionConstants.PATTERN_TYPE;
        } else if (ExpressionConstants.JAVA_TYPE.equals(expressionType)) {
            return ExpressionConstants.JAVA_TYPE;
        } else if (ExpressionConstants.XPATH_TYPE.equals(expressionType)) {
            return ExpressionConstants.XPATH_TYPE;
        } else if (ExpressionConstants.URL_ATTRIBUTE_TYPE.equals(expressionType)) {
            return ExpressionConstants.URL_ATTRIBUTE_TYPE;
        } else if (ExpressionConstants.SEARCH_INDEX_TYPE.equals(expressionType)) {
            return ExpressionConstants.SEARCH_INDEX_TYPE;
        }

        Set<String> cache = new HashSet<String>();
        for (Expression e : getFilteredExpressions()) {
            if (e.getName().equals(input)) {
                cache.add(e.getType());
            }
        }
        if (cache.size() > 1) {
            for (String type : cache) {
                if (type.equals(selectedExpression.getType())) {
                    return type;
                }
            }
            return cache.iterator().next();
        } else if (cache.size() == 1) {
            return cache.iterator().next();
        } else {
            expressionType = CONSTANT_TYPE;
        }

        return expressionType;
    }

    protected void internalRefresh(Object element) {
        Control composite = getControl();
        if (!composite.isDisposed()) {
            ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
            Image icon = getLabelProviderImage(labelProvider, selectedExpression);
            ExpressionTypeLabelProvider expTypeProvider = new ExpressionTypeLabelProvider();
            String desc = expTypeProvider.getText(selectedExpression.getType());
            typeDecoration.setImage(icon);
            typeDecoration.setDescriptionText(desc);
            if (!editing) {
                if (selectedExpression.getName() == null || selectedExpression.getName().isEmpty()) {
                    if (!ExpressionConstants.CONDITION_TYPE.equals(selectedExpression.getType())) {
                        if (typeDecoration.isVisible()) {
                            typeDecoration.hide();
                        }
                    }
                } else {
                    if (!typeDecoration.isVisible()) {
                        typeDecoration.show();
                    }
                }
            }
            refreshMessageDecoration();

        }

    }

    private void refreshMessageDecoration() {
        final Entry<Integer, String> message = getMessageToDisplay();
        if (message != null) {
            messageDecoration.setDescriptionText(message.getValue());
            if (message.getKey() == IStatus.INFO) {
                // Issue with focus
                messageDecoration.setShowOnlyOnFocus(false);
            } else {
                messageDecoration.setShowOnlyOnFocus(false);
            }

            Image icon = getImageForMessageKind(message.getKey());
            if (icon != null) {
                messageDecoration.setImage(icon);
            }

            messageDecoration.show();

        } else {

            messageDecoration.hide();

        }
    }

    private Image getImageForMessageKind(Integer messageKind) {
        switch (messageKind) {
        case IStatus.WARNING:
            return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_WARN_TSK);
        case IStatus.INFO:
            return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_INFO_TSK);
        case IStatus.ERROR:
            return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
        default:
            break;
        }

        return null;
    }

    private Entry<Integer, String> getMessageToDisplay() {
        Entry<Integer, String> errorEntry = null;
        Entry<Integer, String> warningEntry = null;
        Entry<Integer, String> infoEntry = null;
        for (Entry<Integer, String> entry : messages.entrySet()) {
            if (entry.getKey() == IStatus.ERROR && entry.getValue() != null) {
                errorEntry = entry;
            } else if (entry.getKey() == IStatus.WARNING && entry.getValue() != null) {
                warningEntry = entry;
            } else if (entry.getKey() == IStatus.INFO && entry.getValue() != null) {
                infoEntry = entry;
            }
        }
        if (errorEntry != null) {
            return errorEntry;
        } else if (warningEntry != null) {
            return warningEntry;
        } else if (infoEntry != null) {
            return infoEntry;
        } else {
            return null;
        }
    }

    @Override
    public void setLabelProvider(IBaseLabelProvider labelProvider) {
        Assert.isTrue(labelProvider instanceof ExpressionLabelProvider);
        super.setLabelProvider(labelProvider);
    }

    public Text getTextControl() {
        return textControl;
    }

    public ToolItem getButtonControl() {
        return editControl;
    }

    public void addFilter(ViewerFilter viewerFilter) {
        filters.add(viewerFilter);
    }

    public void removeFilter(ViewerFilter viewerFilter) {
        filters.remove(viewerFilter);
    }

    public String getExample() {
        return example;
    }

    public void setExample(String example) {
        this.example = example;
        textControl.setMessage(example);
        textControl.redraw();
    }

    public String getMessage(int messageKind) {
        return messages.get(messageKind);
    }

    public void setMessage(String message, int messageKind) {
        if (IStatus.OK == messageKind) {
            messages.remove(IStatus.ERROR);
            messages.remove(IStatus.WARNING);
        } else {
            if (messageKind == IStatus.WARNING) {
                messages.remove(IStatus.ERROR);
            }
            if (messageKind == IStatus.INFO) {
                messages.remove(IStatus.ERROR);
                messages.remove(IStatus.WARNING);
            }
            messages.put(messageKind, message);
        }
        refresh();
    }

    protected void fireValidationStatusChanged(int newStatus) {
        for (IExpressionValidationListener listener : validationListeners) {
            listener.validationStatusChanged(newStatus);
        }
    }

    @Override
    protected void handleDispose(DisposeEvent event) {
        if (disposeDomain) {
            WorkspaceEditingDomainFactory.INSTANCE.unmapResourceSet((TransactionalEditingDomain) editingDomain);
            editingDomain = null;
            disposeDomain = false;
        }
        if (expressionBinding != null && externalDataBindingContext != null) {
            externalDataBindingContext.removeBinding(expressionBinding);
            expressionBinding.dispose();
        }
        if (internalDataBindingContext != null) {
            internalDataBindingContext.dispose();
        }

        super.handleDispose(event);
    }

    public void setEditingDomain(EditingDomain editingDomain) {
        this.editingDomain = editingDomain;
    }

    public void setMandatoryField(String fieldName, DataBindingContext dbc) {
        mandatoryFieldName = fieldName;
        externalDataBindingContext = dbc;
    }

    public void addExpressionEditorChangedListener(ISelectionChangedListener iSelectionChangedListener) {
        expressionEditorListener.add(iSelectionChangedListener);
    }

    public EReference getExpressionReference() {
        return expressionReference;
    }

    public void setContext(EObject context) {
        this.context = context;
    }

    public void updateAutocompletionProposals() {
        if (expressionNatureProvider != null) {
            Set<Expression> filteredExpressions = getFilteredExpressions();
            autoCompletion.setProposals(filteredExpressions.toArray(new Expression[filteredExpressions.size()]));
        }
    }

    public void setProposalsFiltering(boolean filterProposal) {
        autoCompletion.getContentProposalProvider().setFiltering(filterProposal);
    }

    public ToolItem getEraseControl() {
        return eraseControl;
    }

    protected Converter getNameConverter() {
        Converter nameConverter = new Converter(String.class, String.class) {

            @Override
            public Object convert(Object fromObject) {
                int caretPosition = textControl.getCaretPosition();
                String input = (String) fromObject;
                updateContentType(getContentTypeFromInput(input));
                updateContent(getContentFromInput(input));
                boolean hasBeenExecuted = executeOperation(input);
                refresh();
                if (hasBeenExecuted) {
                    textControl.setSelection(caretPosition, caretPosition);
                }
                return fromObject;
            }
        };
        return nameConverter;
    }

    public void addExpressionValidator(String expressionType, IExpressionValidator comaprisonExpressionValidator) {
        validatorsForType.put(expressionType, comaprisonExpressionValidator);
    }

    public void addExpressionValidationListener(IExpressionValidationListener listener) {
        if (!validationListeners.contains(listener)) {
            validationListeners.add(listener);
        }
    }

    public void validate() {
        if (expressionBinding != null && expressionBinding.getTarget() != null) {
            expressionBinding.validateTargetToModel();
        }
    }

    public void setExternalDataBindingContext(DataBindingContext ctx) {
        this.externalDataBindingContext = ctx;
    }

    @Override
    public void proposalAccepted(IContentProposal proposal) {
        int proposalAcceptanceStyle = autoCompletion.getContentProposalAdapter().getProposalAcceptanceStyle();
        if (proposalAcceptanceStyle == ContentProposalAdapter.PROPOSAL_REPLACE) {
            ExpressionProposal prop = (ExpressionProposal) proposal;
            final Expression copy = EcoreUtil.copy((Expression) prop.getExpression());
            copy.setReturnTypeFixed(selectedExpression.isReturnTypeFixed());
            if (copy.getType().equals(ExpressionConstants.FORM_FIELD_TYPE)) {
                EObject parent = context;
                if (parent == null) {
                    parent = expressionNatureProvider.getContext();
                }
                if (parent instanceof Widget) {
                    final Widget w = (Widget) parent;
                    if (w != null && w instanceof TextFormField && copy.getName().equals("field_" + w.getName())) {
                        String returnTypeModifier = w.getReturnTypeModifier();
                        if (returnTypeModifier != null) {
                            if (w instanceof Duplicable && ((Duplicable) w).isDuplicate()) {
                                returnTypeModifier = List.class.getName();
                            }
                            if (!copy.isReturnTypeFixed()) {
                                copy.setReturnType(returnTypeModifier);
                            }
                        }
                    }
                }
            }

            updateSelection(copy);
            fireSelectionChanged(
                    new SelectionChangedEvent(ExpressionViewer.this, new StructuredSelection(selectedExpression)));
            validate();
        }
    }

    @Override
    public void proposalPopupOpened(BonitaContentProposalAdapter adapter) {
        manageNatureProviderAndAutocompletionProposal(getInput());
    }

    @Override
    public void proposalPopupClosed(BonitaContentProposalAdapter adapter) {

    }

    public IExpressionNatureProvider getExpressionNatureProvider() {
        return expressionNatureProvider;
    }

    @Override
    public boolean isPageFlowContext() {

        return isPageFlowContext;
    }

    @Override
    public void setIsPageFlowContext(boolean isPageFlowContext) {
        this.isPageFlowContext = isPageFlowContext;

    }

    public void setRefactorOperationToExecuteWhenUpdatingContent(AbstractRefactorOperation operation) {
        this.operation = operation;
    }

    private boolean executeOperation(String newValue) {
        boolean hasBeenExecuted = false;
        if (operation != null) {
            operation.setNewValue(newValue);
            IProgressService service = PlatformUI.getWorkbench().getProgressService();
            try {
                service.busyCursorWhile(operation);
                hasBeenExecuted = true;
            } catch (InvocationTargetException e) {
                BonitaStudioLog.error(e);
            } catch (InterruptedException e) {
                BonitaStudioLog.error(e);
            }

        }
        return hasBeenExecuted;
    }

    public void setRemoveOperation(AbstractRefactorOperation removeOperation) {
        this.removeOperation = removeOperation;
    }

    private boolean executeRemoveOperation(CompoundCommand cc) {
        boolean isExecuted = false;
        if (removeOperation != null) {
            removeOperation.setCompoundCommand(cc);
            IProgressService service = PlatformUI.getWorkbench().getProgressService();
            try {
                service.busyCursorWhile(removeOperation);
                isExecuted = true;
            } catch (InvocationTargetException e) {
                BonitaStudioLog.error(e);
            } catch (InterruptedException e) {
                BonitaStudioLog.error(e);
            }

        }
        return isExecuted;
    }

}