org.csstudio.sds.ui.internal.dynamicswizard.SimpleChannelPage.java Source code

Java tutorial

Introduction

Here is the source code for org.csstudio.sds.ui.internal.dynamicswizard.SimpleChannelPage.java

Source

/*
 * Copyright (c) 2008 Stiftung Deutsches Elektronen-Synchrotron,
 * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
 *
 * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
 * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
 * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
 * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
 * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
 * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
 * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
 * OR MODIFICATIONS.
 * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
 * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
 * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
 * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
 */
package org.csstudio.sds.ui.internal.dynamicswizard;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.csstudio.dal.ui.dnd.rfc.IProcessVariableAdressReceiver;
import org.csstudio.dal.ui.dnd.rfc.IShowControlSystemDialogStrategy;
import org.csstudio.dal.ui.dnd.rfc.ProcessVariableExchangeUtil;
import org.csstudio.domain.common.LayoutUtil;
import org.csstudio.platform.model.pvs.DalPropertyTypes;
import org.csstudio.platform.model.pvs.IProcessVariableAddress;
import org.csstudio.platform.model.pvs.IProcessVariableAdressProvider;
import org.csstudio.platform.model.pvs.ProcessVariableAdressFactory;
import org.csstudio.sds.internal.rules.NullRule;
import org.csstudio.sds.internal.rules.ParameterDescriptor;
import org.csstudio.sds.internal.rules.RuleDescriptor;
import org.csstudio.sds.internal.rules.RuleService;
import org.csstudio.sds.model.DynamicsDescriptor;
import org.csstudio.sds.model.PropertyTypesEnum;
import org.csstudio.sds.ui.SdsUiPlugin;
import org.csstudio.sds.ui.properties.IPropertyDescriptor;
import org.csstudio.sds.util.ChannelReferenceValidationException;
import org.csstudio.sds.util.ChannelReferenceValidationUtil;
import org.csstudio.sds.util.SelectionUtil;
import org.csstudio.ui.util.CustomMediaFactory;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TableViewerEditor;
import org.eclipse.jface.viewers.TableViewerFocusCellManager;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
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.Group;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;

/**
 * WizardPage implementation, which enables the user to configure a simple
 * channel.
 *
 * @author Sven Wende
 */
public final class SimpleChannelPage extends WizardPage {

    /**
     * A table viewer, which is used to configure input parameters.
     */
    private TableViewer _channelTableViewer;

    /**
     * A table viewer, which is used to configure output parameters.
     */
    private TableViewer _outputChannelTableViewer;

    /**
     * Flag that signals if the definition of output channels should
     * automatically lead to the definition of an input channel.
     */
    private final boolean _isLinkOutput = true;

    /**
     * The dynamics descriptor that is edited.
     */
    private final DynamicsDescriptor _dynamicsDescriptor;

    /**
     * The model for the channel table.
     */
    private InputChannelTableModel _inputChannelTableModel;

    /**
     * Names of existing aliases that can be used in channel names.
     */
    private final Map<String, String> _aliases;

    /**
     * A tree viewer, which shows the available rules.
     */
    private TreeViewer _rulesViewer;
    /**
     * Checkbox for using only the connection states.
     */
    private Button _useOnlyConnectionsCheckBox;
    /**
     * The selected rule.
     */
    private RuleDescriptor _selectedRule;

    private Text _rulePattern;

    private final PropertyTypesEnum _propertyType;

    private Text _ruleDescriptionText;

    /**
     * An action, which removes an input channel from the configuration.
     *
     * @author Sven Wende
     */
    private final class RemoveChannelAction extends Action {
        /**
         * Constructor.
         */
        public RemoveChannelAction() {
            super("Remove Parameter", CustomMediaFactory.getInstance()
                    .getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID, "icons/parameter_remove.gif"));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void run() {
            final IStructuredSelection sel = (IStructuredSelection) _channelTableViewer.getSelection();
            if (sel != null && sel.getFirstElement() != null) {
                _inputChannelTableModel.removeRow((InputChannelTableRow) sel.getFirstElement());
                _channelTableViewer.refresh();
            }
        }
    }

    /**
     * An action, which adds a type hint to the selected channel parameter.
     *
     * @author Sven Wende
     */
    protected final class AppendTypeHintAction extends Action {
        /**
         * The {@link DalPropertyTypes}.
         */
        private final DalPropertyTypes _typeHint;

        /**
         * Constructor.
         *
         * @param typeHint
         *            The {@link DalPropertyTypes}
         */
        protected AppendTypeHintAction(final DalPropertyTypes typeHint) {
            super(typeHint.toString(), CustomMediaFactory.getInstance()
                    .getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID, "icons/getas.gif"));
            assert typeHint != null;
            _typeHint = typeHint;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void run() {
            final IStructuredSelection sel = (IStructuredSelection) _channelTableViewer.getSelection();

            if (sel != null && sel.getFirstElement() != null && !sel.isEmpty()) {
                for (final Object o : sel.toArray()) {
                    final InputChannelTableRow row = (InputChannelTableRow) o;

                    final String rowValue = row.getChannel();

                    if (rowValue != null && rowValue.length() > 0) {
                        row.setChannel(applyTypeHint(rowValue));
                    }
                }

                _channelTableViewer.refresh();
            }
        }

        /**
         * Adds the type hint to the channel url.
         *
         * @param channel
         *            the existing channel url
         * @return a enriched channel url
         */
        private String applyTypeHint(final String channel) {
            String result = null;

            // try to replace existing type hint
            final String newPattern = ", " + _typeHint.toPortableString();
            for (final DalPropertyTypes dalType : DalPropertyTypes.values()) {
                final String oldPattern = ", " + dalType.toPortableString();

                if (dalType != _typeHint && channel.indexOf(oldPattern) > 0) {
                    result = channel.replace(oldPattern, newPattern);
                }
            }

            // if there was no existing type hint, just add the new one
            if (result == null) {
                result = channel + newPattern;
            }

            return result;
        }
    }

    /**
     * An action, which adds an input channel to the configuration.
     *
     * @author Sven Wende
     */
    protected final class AddInputChannelAction extends Action {
        /**
         * Constructor.
         */
        protected AddInputChannelAction() {
            super("Add Parameter", CustomMediaFactory.getInstance()
                    .getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID, "icons/parameter_in.gif"));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void run() {
            addInputChannel("<<Channel>>");
        }
    }

    /**
     * An action, which adds an output channel to the configuration.
     *
     * @author Alexander Will
     */
    protected final class AddOutputChannelAction extends Action {
        /**
         * Constructor.
         */
        protected AddOutputChannelAction() {
            super("Add Output Channel", CustomMediaFactory.getInstance()
                    .getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID, "icons/parameter_out.gif"));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void run() {
            addOutputChannel("<<Channel>>");
        }
    }

    /**
     * Label provider for the channel tables.
     *
     * @author Sven Wende
     *
     */
    protected final class ChannelTableLabelProvider extends ColumnLabelProvider {

        /**
         * FIXME: Momentan ist update() nur wegen eines Workarrounds
         * berschrieben. Umstellen des Labelproviders auf
         * TableColumnViewerLabelProvider.class, sobald diese public ist.
         * {@inheritDoc}
         */
        @Override
        public void update(final ViewerCell cell) {
            final Object element = cell.getElement();
            final int index = cell.getColumnIndex();
            cell.setText(getText(element, index));
            final Image image = getImage(element, index);
            cell.setImage(image);
            cell.setBackground(getBackground(element));
            cell.setForeground(getForeground(element, index));
            cell.setFont(getFont(element, index));
        }

        /**
         * Returns the text to display.
         *
         * @param element
         *            the current element
         * @param columnIndex
         *            the current column index
         * @return The text to display in the viewer
         */
        private String getText(final Object element, final int columnIndex) {
            final InputChannelTableRow row = (InputChannelTableRow) element;
            String result = "";
            switch (columnIndex) {
            case 0:
                result = row.getDescription();
                break;
            case 1:
                result = row.getChannel();
                break;
            case 2:
                result = row.getDefaultValueAsString();
                break;
            default:
                break;
            }
            return result;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getToolTipText(final Object element) {
            String tooltip = "";
            final InputChannelTableRow row = (InputChannelTableRow) element;

            final String rawChannel = row.getChannel();

            try {
                // try to resolve the name (this should replace all aliases)
                final String channel = ChannelReferenceValidationUtil.createCanonicalName(rawChannel, _aliases);

                tooltip = channel + " [STRING]";
            } catch (final ChannelReferenceValidationException e) {
                tooltip = e.getMessage();
            }

            return tooltip;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Point getToolTipShift(final Object object) {
            return new Point(5, 5);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int getToolTipDisplayDelayTime(final Object object) {
            return 100;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int getToolTipTimeDisplayed(final Object object) {
            return 10000;
        }

        /**
         * Returns the font, which is used to display the channel informations.
         *
         * @param element
         *            The current element
         * @param column
         *            The current column index
         * @return The font
         */
        private Font getFont(final Object element, final int column) {
            int style = SWT.BOLD;

            final InputChannelTableRow row = (InputChannelTableRow) element;

            if (column == 1) {
                if (row.getDescription() == null || row.getDescription().equals("")) {
                    style = SWT.ITALIC;
                } else {
                    style = SWT.NONE;
                }
            }
            return CustomMediaFactory.getInstance().getDefaultFont(style);
        }

        /**
         * returns the foreground color for a cell.
         *
         * @param element
         *            The current element
         * @param column
         *            The current column index
         * @return The foreground color
         */
        private Color getForeground(final Object element, final int column) {
            RGB rgb = new RGB(0, 0, 0);

            if (column == 1) {
                final InputChannelTableRow row = (InputChannelTableRow) element;

                if (row.getDescription() == null || row.getDescription().equals("")) {
                    rgb = new RGB(200, 200, 200);
                } else {
                    if (!ChannelReferenceValidationUtil.testValidity(row.getChannel())) {
                        rgb = new RGB(255, 160, 160);
                    }
                }
            }
            return CustomMediaFactory.getInstance().getColor(rgb);
        }

        /**
         * Returns the Image for a cell.
         *
         * @param element
         *            The current element
         * @param index
         *            The current column index
         * @return The Image for the cell
         */
        private Image getImage(final Object element, final int index) {
            Image result = null;

            if (index == 0) {
                final InputChannelTableRow row = (InputChannelTableRow) element;

                if (row.getParameterType() == ParameterType.IN) {
                    result = CustomMediaFactory.getInstance().getImageFromPlugin(SdsUiPlugin.PLUGIN_ID,
                            "icons/parameter_in.gif"); //$NON-NLS-1$
                } else if (row.getParameterType() == ParameterType.OUT) {
                    result = CustomMediaFactory.getInstance().getImageFromPlugin(SdsUiPlugin.PLUGIN_ID,
                            "icons/parameter_out.gif"); //$NON-NLS-1$
                }
            }

            return result;
        }
    }

    /**
     * Content provider for the input channel table.
     *
     * @author Sven Wende
     *
     */
    protected final class ChannelTableContentProvider implements IStructuredContentProvider {
        /**
         * {@inheritDoc}
         */
        @Override
        public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {

        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Object[] getElements(final Object parent) {
            return _inputChannelTableModel.getAllRows().toArray();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void dispose() {

        }
    }

    /**
     * Cell modifier for the channel tables.
     *
     * @author Sven Wende
     *
     */
    protected class ChannelTableCellModifier implements ICellModifier {
        /**
         * Constructor.
         */
        protected ChannelTableCellModifier() {
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public final boolean canModify(final Object element, final String property) {
            return true;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public final Object getValue(final Object element, final String property) {
            Object result = null;

            final InputChannelTableRow row = (InputChannelTableRow) element;

            switch (findColumnIndex(property)) {
            case 0:
                result = row.getChannel();
                break;
            case 1:
                result = row.getChannel();
                break;
            default:
                break;
            }
            assert result != null : "result!=null"; //$NON-NLS-1$;

            return result;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public final void modify(final Object element, final String property, final Object value) {
            InputChannelTableRow row;

            // @see ICellModifier#modify(element, property, value)
            if (element instanceof Item) {
                row = (InputChannelTableRow) ((Item) element).getData();
            } else {
                row = (InputChannelTableRow) element;
            }

            switch (findColumnIndex(property)) {
            case 1:
                final String rawInput = (String) value;

                String channel = "";
                // We take the plain raw input by default. If the entered name
                // does not reference any aliases, we try to
                // validate it using the process variable name parsers, which
                // might automatically add a control system prefix.
                final boolean valid = ChannelReferenceValidationUtil.testValidity(rawInput);

                if (valid) {
                    final List<String> requiredAliasNames = ChannelReferenceValidationUtil
                            .getRequiredAliasNames(rawInput);

                    if (requiredAliasNames.isEmpty()) {
                        final IProcessVariableAddress pv = ProcessVariableExchangeUtil
                                .parseProcessVariableAdress(rawInput, true);

                        if (pv != null) {
                            channel = pv.getFullName();
                        }
                    } else {
                        channel = rawInput;
                    }
                } else {
                    channel = rawInput;
                }

                // setChannelHook(row, channel);
                row.setChannel(channel);
                break;
            default:
                break;
            }

            _channelTableViewer.refresh();
            _channelTableViewer.setSelection(null);
        }

        /**
         * Hook method that is called before the channel name is actually set.
         * Subclasses may overwrite in order to perform custom actions.
         *
         * @param descriptor
         *            the parameter descriptor.
         * @param channel
         *            the new channel name.
         */
        protected void setChannelHook(final ParameterDescriptor descriptor, final String channel) {
        }

        /**
         * Gets the colunmn index for the specified property.
         *
         * @param property
         *            the property
         *
         * @return the column index
         */
        private int findColumnIndex(final String property) {
            int result = 0;
            // Find the index of the column
            final Object[] columnHeaders = _channelTableViewer.getColumnProperties();
            for (int i = 0; i < columnHeaders.length; i++) {
                if (columnHeaders[i].equals(property)) {
                    result = i;
                }
            }

            return result;
        }
    }

    /**
     * The {@link EditingSupport} for the columns of the property table.
     *
     * @author Kai Meyer
     *
     */
    private final class CustomEditingSupport extends EditingSupport {

        /**
         * The {@link Table} where this {@link EditingSupport} is embedded.
         */
        private final Table _table;

        /**
         * Determines if this {@link EditingSupport} is for the <i>Channel</i>
         * column.
         */
        private final boolean _channelColumn;

        /**
         * Constructor.
         *
         * @param viewer
         *            The {@link ColumnViewer} for this {@link EditingSupport}.
         * @param table
         *            The {@link Table}
         * @param channelColumn
         *            True if this {@link EditingSupport} is for the <i>Channel</i>
         *            column
         */
        private CustomEditingSupport(final ColumnViewer viewer, final Table table, final boolean channelColumn) {
            super(viewer);
            _table = table;
            _channelColumn = channelColumn;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected boolean canEdit(final Object element) {
            return true;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected CellEditor getCellEditor(final Object element) {
            return new TextCellEditor(_table);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected Object getValue(final Object element) {
            if (element instanceof InputChannelTableRow) {
                final InputChannelTableRow row = (InputChannelTableRow) element;
                if (_channelColumn) {
                    return row.getChannel();
                }
                return row.getDefaultValue();
            }
            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void setValue(final Object element, final Object value) {
            if (element instanceof InputChannelTableRow) {
                if (_channelColumn) {
                    ((InputChannelTableRow) element).setChannel(value.toString());
                    _channelTableViewer.getCellModifier().modify(element, "PROP_NAME", value);
                } else {
                    String newValue = "";
                    if (value != null) {
                        newValue = value.toString();
                    }
                    ((InputChannelTableRow) element).setDefaultValue(newValue);
                }
            }
            this.getViewer().refresh();
        }
    }

    /**
     * Constructor.
     *
     * @param pageName
     *            the name of the page
     * @param dynamicsDescriptor
     *            the {@link DynamicsDescriptor}
     * @param propertyDescriptor
     *            the {@link IPropertyDescriptor}
     * @param aliases
     *            the aliases
     */
    public SimpleChannelPage(final String pageName, final DynamicsDescriptor dynamicsDescriptor,
            final PropertyTypesEnum propertyType, final Map<String, String> aliases) {
        super(pageName);
        assert dynamicsDescriptor != null;
        assert aliases != null;
        assert propertyType != null : "propertyType != null";
        _propertyType = propertyType;
        setTitle("Dynamics Wizard");
        setDescription("Use this wizard to configure the dynamic behaviour of your properties");
        _dynamicsDescriptor = dynamicsDescriptor;
        _aliases = aliases;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void createControl(final Composite parent) {
        final Composite c = new Composite(parent, SWT.None);
        c.setLayout(new GridLayout(1, false));

        _useOnlyConnectionsCheckBox = new Button(c, SWT.CHECK);
        _useOnlyConnectionsCheckBox.setText("Use only connection states");
        _useOnlyConnectionsCheckBox.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                executeCheckBoxStateChange(_useOnlyConnectionsCheckBox.getSelection());
            }
        });

        _rulePattern = new Text(c, SWT.SEARCH);
        _rulePattern.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
        _rulePattern.setMessage("Rule Pattern");

        _rulesViewer = createRuleControl(c);
        _channelTableViewer = createInputChannelsTable(c);

        createAliasInformation(c);

        // initialize the widgets
        if (_dynamicsDescriptor.getRuleId() != null) {
            _selectedRule = RuleService.getInstance().getRuleDescriptor(_dynamicsDescriptor.getRuleId());
            if (_selectedRule != null) {
                _rulesViewer.setSelection(new StructuredSelection(_selectedRule));
            }
            updateChannelTableModel();
        }

        _useOnlyConnectionsCheckBox.setSelection(_dynamicsDescriptor.isUsingOnlyConnectionStates());
        this.executeCheckBoxStateChange(_useOnlyConnectionsCheckBox.getSelection());

        // important for wizards -> set the control
        setControl(c);
    }

    /**
     * @param c
     * @return
     */
    private Text createDescriptionControl(final Composite c) {
        final Text text = new Text(c, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | SWT.BORDER | SWT.READ_ONLY);
        //        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        text.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
        text.setText("");
        return text;
    }

    /**
     * Updates the viewer for the rules depending on given choice.
     *
     * @param choice
     *            <code>true</code> if the viewer should be disabled,
     *            <code>false</code> otherwise.
     */
    private void executeCheckBoxStateChange(final boolean choice) {
        _rulesViewer.getTree().setEnabled(!choice);
        if (choice) {
            _selectedRule = RuleService.getInstance().getRuleDescriptor(NullRule.ID);
        } else {
            _selectedRule = RuleService.getInstance().getRuleDescriptor(_dynamicsDescriptor.getRuleId());
        }
        updateChannelTableModel();
    }

    /**
     * Creates a control, which contains a filtered list of rules.
     *
     * @param parent
     *            the parent composite
     * @return the control
     */
    private TreeViewer createRuleControl(final Composite parent) {
        final Group group = new Group(parent, SWT.NONE);
        group.setLayout(new GridLayout(2, false));
        group.setText("Rules / Scripts");
        group.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.CENTER)
                .hint(SWT.DEFAULT, 120).create());

        final TreeViewer viewer = new TreeViewer(group,
                SWT.DROP_DOWN | SWT.READ_ONLY | SWT.SCROLL_LINE | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);

        viewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(false, true).create());
        _ruleDescriptionText = createDescriptionControl(group);

        viewer.setLabelProvider(new WorkbenchLabelProvider());
        viewer.setContentProvider(new BaseWorkbenchContentProvider() {
            @SuppressWarnings("unchecked")
            @Override
            public Object[] getElements(final Object element) {
                return ((Collection<RuleDescriptor>) element).toArray();
            }
        });
        ColumnViewerToolTipSupport.enableFor(viewer);

        final List<RuleDescriptor> rules = RuleService.getInstance().getRegisteredRuleDescriptors();

        Collections.sort(rules, new Comparator<RuleDescriptor>() {
            @Override
            public int compare(final RuleDescriptor r1, final RuleDescriptor r2) {
                return r1.getDescription().compareTo(r2.getDescription());
            }
        });
        viewer.setInput(rules);

        // setup a filter for the rules
        viewer.addFilter(new ViewerFilter() {

            @Override
            public boolean select(final Viewer viewer, final Object parentElement, final Object element) {
                final RuleDescriptor ruleDescriptor = (RuleDescriptor) element;

                final PropertyTypesEnum[] ruleReturnType = ruleDescriptor.getCompatiblePropertyTypes();

                for (final PropertyTypesEnum type : ruleReturnType) {
                    if (type.equals(_propertyType)) {
                        return true;
                    }
                }

                return false;
            }

        });

        // Filter for Pattern set by User.
        viewer.addFilter(new ViewerFilter() {

            @Override
            public boolean select(final Viewer viewer, final Object parentElement, final Object element) {
                final RuleDescriptor ruleDescriptor = (RuleDescriptor) element;
                final String pattern = _rulePattern.getText();
                if (pattern == null || pattern.length() < 1) {
                    return true;
                }
                return ruleDescriptor.getDescription().toLowerCase().matches(pattern.replace("$", "\\$")
                        .replace(".", "\\.").replace("*", ".*").replace("?", ".?").toLowerCase() + ".*");
            }

        });

        // listener to update rule list for new rule patterns set by user.
        _rulePattern.addKeyListener(new KeyListener() {

            @Override
            public void keyPressed(final KeyEvent e) {
            }

            @Override
            public void keyReleased(final KeyEvent e) {
                viewer.refresh();
            }

        });

        // add a selection listener that updates the channel table when the rule
        // changes
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                final IStructuredSelection sel = (IStructuredSelection) event.getSelection();

                if (sel.getFirstElement() != null) {
                    final RuleDescriptor descriptor = (RuleDescriptor) sel.getFirstElement();
                    _selectedRule = descriptor;

                    String description = descriptor.getRule().getDescription();
                    if (description == null || description.isEmpty()) {
                        description = "no description available";
                    }
                    _ruleDescriptionText.setText(description);
                    updateChannelTableModel();
                }
            }

        });

        return viewer;
    }

    /**
     * Creates a table viewer, which enables the user to enter typed input
     * channels.
     *
     * @param parent
     *            the parent composite
     *
     * @return the created viewer
     */
    private TableViewer createInputChannelsTable(final Composite parent) {
        final Group group = new Group(parent, SWT.NONE);
        group.setLayout(LayoutUtil.createGridLayout(1, 0, 0, 0));
        group.setText("Input Channels");
        group.setLayoutData(LayoutUtil.createGridDataForHorizontalFillingCell(300));

        final TableViewer viewer = createChannelTable(group);

        // enable Tooltip support
        ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);

        // cell modifiers
        final ICellModifier cellModifier = new ChannelTableCellModifier() {
            @Override
            protected void setChannelHook(final ParameterDescriptor descriptor, final String channel) {
                if (_isLinkOutput) {
                    if (_dynamicsDescriptor.getOutputChannel() != null
                            && _dynamicsDescriptor.getOutputChannel().equals(descriptor)) {
                        _dynamicsDescriptor.getOutputChannel().setChannel(channel);
                    }
                }

                viewer.setSelection(null);
            }

        };

        viewer.setCellModifier(cellModifier);

        viewer.setContentProvider(new ChannelTableContentProvider());
        viewer.setLabelProvider(new ChannelTableLabelProvider());

        // Input aufbereiten
        _inputChannelTableModel = createChannelTableModel(_dynamicsDescriptor);
        viewer.setInput(_inputChannelTableModel);

        createPopupMenu(viewer.getControl());
        return viewer;
    }

    /**
     * Creates the widgets to display the alias informations.
     *
     * @param parent
     *            The parent for the widgets
     */
    private void createAliasInformation(final Composite parent) {
        final Composite c = new Composite(parent, SWT.NONE);
        c.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
        c.setLayout(new GridLayout(2, false));

        final Label header = new Label(c, SWT.NONE);
        final GridData gd = GridDataFactory.fillDefaults().grab(true, true).create();
        gd.horizontalSpan = 2;
        header.setLayoutData(gd);
        header.setFont(CustomMediaFactory.getInstance().getDefaultFont(SWT.BOLD));
        header.setText("Available Aliases / Macros");

        for (final String alias : _aliases.keySet()) {
            final Label left = new Label(c, SWT.NONE);
            left.setLayoutData(GridDataFactory.fillDefaults().create());
            left.setForeground(CustomMediaFactory.getInstance().getColor(new RGB(0, 0, 255)));
            left.setText("$" + alias + "$");

            final Label right = new Label(c, SWT.NONE);
            right.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
            right.setText("--> " + _aliases.get(alias));
        }
    }

    /**
     * Creates the popup menu.
     *
     * @param control
     *            The parent control for the menu
     * @return The {@link Menu}
     */
    private Menu createPopupMenu(final Control control) {
        final MenuManager popupMenu = new MenuManager();

        // channel actions
        IAction a = new AddInputChannelAction();
        popupMenu.add(a);
        a = new AddOutputChannelAction();
        popupMenu.add(a);
        a = new RemoveChannelAction();
        popupMenu.add(a);

        // "get as [TYPE]" actions
        final MenuManager subMenu = new MenuManager("Get As");
        popupMenu.add(subMenu);

        for (final DalPropertyTypes type : DalPropertyTypes.values()) {
            a = new AppendTypeHintAction(type);
            subMenu.add(a);
        }

        final Menu menu = popupMenu.createContextMenu(control);
        control.setMenu(menu);
        return menu;
    }

    /**
     * Creates the model for the channel table.
     *
     * @param dynamicsDescriptor
     *            The {@link DynamicsDescriptor}
     * @return the created {@link InputChannelTableModel}
     */
    private static InputChannelTableModel createChannelTableModel(final DynamicsDescriptor dynamicsDescriptor) {
        final InputChannelTableModel model = new InputChannelTableModel();

        // input channels
        for (final ParameterDescriptor descriptor : dynamicsDescriptor.getInputChannels()) {
            final String inputChannel = new String(descriptor.getChannel());
            model.addRowForInputChannel(new InputChannelTableRow(ParameterType.IN, "", inputChannel));
        }

        // output channel
        if (dynamicsDescriptor.getOutputChannel() != null) {
            final String outputChannel = new String(dynamicsDescriptor.getOutputChannel().getChannel());

            if (outputChannel != null && outputChannel.length() > 0) {
                model.addRowForOutputChannel(
                        new InputChannelTableRow(ParameterType.OUT, "Output Channel", outputChannel));
            }
        }

        return model;
    }

    /**
     * Updates the model.
     */
    private void updateChannelTableModel() {
        if (_selectedRule != null) {
            _inputChannelTableModel.clearInputChannelDescriptions();
            for (int i = 0; i < _selectedRule.getParameterDescriptions().length; i++) {
                _inputChannelTableModel.setInputChannelDescription(i, _selectedRule.getParameterDescriptions()[i]);
                if (_selectedRule.getRuleId().equals(_dynamicsDescriptor.getRuleId())
                        && _dynamicsDescriptor.getInputChannels().length > i) {
                    _inputChannelTableModel.setInputChannelValue(i,
                            _dynamicsDescriptor.getInputChannels()[i].getValue());
                } else {
                    _inputChannelTableModel.setInputChannelValue(i, "");
                }
            }
            _channelTableViewer.refresh();
        }
    }

    /**
     * Creates a table viewer for managing channels.
     *
     * @param parent
     *            The parent composite.
     *
     * @return The created viewer.
     */
    private TableViewer createChannelTable(final Composite parent) {
        // define column names
        final String[] columnNames = new String[] { "PROP_DESCRIPTION", "PROP_NAME", "PROP_VALUE" }; //$NON-NLS-1$ //$NON-NLS-2$

        // create table
        final Table table = new Table(parent,
                SWT.FULL_SELECTION | SWT.HIDE_SELECTION | SWT.DOUBLE_BUFFERED | SWT.SCROLL_PAGE);
        table.setLinesVisible(true);
        table.setLayoutData(LayoutUtil.createGridDataForFillingCell());
        table.setHeaderVisible(true);

        // create viewer
        final TableViewer viewer = new TableViewer(table);

        TableViewerColumn tvColumn;
        tvColumn = new TableViewerColumn(viewer, SWT.NONE);
        tvColumn.getColumn().setText("Description");
        tvColumn.getColumn().setMoveable(false);
        tvColumn.getColumn().setWidth(200);
        tvColumn = new TableViewerColumn(viewer, SWT.NONE);
        tvColumn.getColumn().setText("Channel");
        tvColumn.getColumn().setMoveable(false);
        tvColumn.getColumn().setWidth(300);
        EditingSupport editingSupport = new CustomEditingSupport(viewer, table, true);
        tvColumn.setEditingSupport(editingSupport);
        tvColumn = new TableViewerColumn(viewer, SWT.NONE);
        tvColumn.getColumn().setText("Default Value");
        tvColumn.getColumn().setMoveable(false);
        tvColumn.getColumn().setWidth(200);
        editingSupport = new CustomEditingSupport(viewer, table, false);
        tvColumn.setEditingSupport(editingSupport);

        viewer.setUseHashlookup(true);

        // define column properties
        viewer.setColumnProperties(columnNames);

        // configure keyboard support
        final TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(viewer,
                new FocusCellOwnerDrawHighlighter(viewer));

        final ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(viewer) {
            @Override
            protected boolean isEditorActivationEvent(final ColumnViewerEditorActivationEvent event) {
                return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
                        || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION
                        || event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED
                                && event.keyCode == SWT.F2
                        || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
            }
        };

        TableViewerEditor.create(viewer, focusCellManager, actSupport,
                ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
                        | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);

        // DnD
        ProcessVariableExchangeUtil.addProcessVariableAddressDropSupport(viewer.getControl(),
                DND.DROP_MOVE | DND.DROP_COPY, new IProcessVariableAdressReceiver() {
                    public void receive(final IProcessVariableAddress[] pvs, final DropTargetEvent event) {
                        for (final IProcessVariableAddress pv : pvs) {
                            addInputChannel(pv.getFullName());
                        }
                    }
                }, new IShowControlSystemDialogStrategy() {

                    public boolean showControlSystem(final String rawName) {
                        // only popup the dialog if there are no aliases used
                        // within the raw string
                        final boolean show = ChannelReferenceValidationUtil.getRequiredAliasNames(rawName)
                                .isEmpty();
                        return show;
                    }

                });

        ProcessVariableExchangeUtil.addProcessVariableAdressDragSupport(viewer.getControl(),
                DND.DROP_MOVE | DND.DROP_COPY, new IProcessVariableAdressProvider() {
                    @Override
                    public List<IProcessVariableAddress> getProcessVariableAdresses() {
                        final List<ParameterDescriptor> parameterDescriptors = SelectionUtil.getInstance()
                                .getObjectsFromSelection(viewer.getSelection());

                        final List<IProcessVariableAddress> result = new ArrayList<IProcessVariableAddress>();

                        for (final ParameterDescriptor d : parameterDescriptors) {
                            final IProcessVariableAddress pv = ProcessVariableAdressFactory.getInstance()
                                    .createProcessVariableAdress(d.getChannel());
                            result.add(pv);
                        }
                        return result;
                    }

                    @Override
                    public IProcessVariableAddress getPVAdress() {
                        final List<IProcessVariableAddress> all = getProcessVariableAdresses();
                        if (all.size() > 0) {
                            return all.get(0);
                        }
                        return null;
                    }

                });

        return viewer;
    }

    /**
     * Add a output channel.
     *
     * @param channelName
     *            The output channel name.
     */
    private void addOutputChannel(final String channelName) {
        final ParameterDescriptor descriptor = new ParameterDescriptor();
        descriptor.setChannel(channelName);
        _dynamicsDescriptor.setOutputChannel(descriptor);

        final InputChannelTableRow row = new InputChannelTableRow(ParameterType.OUT, "OUT", channelName);
        _inputChannelTableModel.addRowForOutputChannel(row);
        _channelTableViewer.refresh();

        _outputChannelTableViewer.refresh();
    }

    /**
     * Add a input channel.
     *
     * @param channelName
     *            The input channel name.
     */
    private void addInputChannel(final String channelName) {
        final IProcessVariableAddress pv = ProcessVariableAdressFactory.getInstance()
                .createProcessVariableAdress(channelName);

        final InputChannelTableRow row = new InputChannelTableRow(ParameterType.IN, "", pv.getFullName());
        _inputChannelTableModel.addRowForInputChannel(row);
        _channelTableViewer.refresh();
    }

    /**
     * Finishes the editing.
     *
     * @param dynamicsDescriptor
     *            The {@link DynamicsDescriptor}
     */
    public void performFinish(final DynamicsDescriptor dynamicsDescriptor) {
        // setup rule
        if (_selectedRule != null) {
            dynamicsDescriptor.setRuleId(_selectedRule.getRuleId());
        }
        final boolean selection = _useOnlyConnectionsCheckBox.getSelection();
        dynamicsDescriptor.setUsingOnlyConnectionStates(selection);
        // setup IN channels
        for (final InputChannelTableRow row : _inputChannelTableModel.getRowsWithContent(ParameterType.IN)) {
            dynamicsDescriptor.addInputChannel(new ParameterDescriptor(row.getChannel(), row.getDefaultValue()));
        }

        // setup OUT channels
        final List<InputChannelTableRow> outParameter = _inputChannelTableModel
                .getRowsWithContent(ParameterType.OUT);
        if (outParameter.size() > 0) {
            final InputChannelTableRow row = outParameter.get(0);
            dynamicsDescriptor.setOutputChannel(new ParameterDescriptor(row.getChannel(), row.getDefaultValue()));
        }
    }
}