de.ovgu.featureide.fm.ui.editors.ConstraintDialog.java Source code

Java tutorial

Introduction

Here is the source code for de.ovgu.featureide.fm.ui.editors.ConstraintDialog.java

Source

/* FeatureIDE - An IDE to support feature-oriented software development
 * Copyright (C) 2005-2012  FeatureIDE team, University of Magdeburg
 *
 * 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 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/.
 *
 * See http://www.fosd.de/featureide/ for further information.
 */
package de.ovgu.featureide.fm.ui.editors;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.PlatformUI;
import org.prop4j.Node;
import org.prop4j.NodeReader;
import org.prop4j.NodeWriter;
import org.prop4j.Not;
import org.prop4j.SatSolver;
import org.sat4j.specs.TimeoutException;

import de.ovgu.featureide.fm.core.Constraint;
import de.ovgu.featureide.fm.core.Feature;
import de.ovgu.featureide.fm.core.FeatureModel;
import de.ovgu.featureide.fm.core.FeatureStatus;
import de.ovgu.featureide.fm.core.Operator;
import de.ovgu.featureide.fm.core.editing.Comparison;
import de.ovgu.featureide.fm.core.editing.ModelComparator;
import de.ovgu.featureide.fm.ui.FMUIPlugin;
import de.ovgu.featureide.fm.ui.editors.featuremodel.GUIDefaults;
import de.ovgu.featureide.fm.ui.editors.featuremodel.operations.ConstraintCreateOperation;
import de.ovgu.featureide.fm.ui.editors.featuremodel.operations.ConstraintEditOperation;

/**
 * A simple editor for propositional constraints written below the feature
 * diagram.
 * 
 * @author Christian Becker
 * @author Thomas Thuem
 * @author David Broneske
 * @author Fabian Benduhn
 */

public class ConstraintDialog implements GUIDefaults {

    private static final String FILTERTEXT = "type filter text";
    private Shell shell;

    private String initialConstraint;

    private Label errorMarker;
    private Text errorMessage;
    private String titleText;
    private String headerText;
    private Group featureGroup;
    private StyledText searchFeatureText;
    private Table featureTable;

    private Group buttonGroup;
    private Composite constraintTextComposite;
    private Text constraintText;
    private FeatureModel featureModel;
    private int x, y;
    private Button okButton;
    private Constraint constraint;

    /**
     * 
     * @param featuremodel
     * @param constraint
     */
    public ConstraintDialog(final FeatureModel featuremodel, final Constraint constraint) {
        this.constraint = constraint;
        this.featureModel = featuremodel;

        if (constraint == null) {
            titleText = "Create Propositional Constraint";
            headerText = "Create new Constraint";
            initialConstraint = "";

        } else {
            titleText = "Edit Propositional Constraint";
            headerText = "Edit your Constraint";

            initialConstraint = constraint.getNode().toString(NodeWriter.textualSymbols);
        }

        initShell();
        initHead();
        initFeatureGroup(featuremodel);
        initButtonGroup();
        initConstraintText();
        initBottom(featuremodel, constraint);
        printHeaderText(headerText);
        constraintText.setFocus();
        constraintText.setSelection(constraintText.getCharCount());
        shell.open();
        if (constraint != null)
            validate();
    }

    /**
     * initializes the shell
     */
    private void initShell() {
        shell = new Shell(Display.getCurrent());
        shell.setText(titleText);
        shell.setImage(FEATURE_SYMBOL);
        shell.setSize(500, 585);
        GridLayout shellLayout = new GridLayout();
        shellLayout.marginWidth = 0;
        shellLayout.marginHeight = 0;
        shell.setLayout(shellLayout);

        Monitor primary = shell.getDisplay().getPrimaryMonitor();
        Rectangle bounds = primary.getBounds();
        Rectangle rect = shell.getBounds();
        int x = bounds.x + (bounds.width - rect.width) / 2;
        int y = bounds.y + (bounds.height - rect.height) / 2;
        shell.setLocation(x, y);
        shell.addListener(SWT.Traverse, new Listener() {
            public void handleEvent(Event event) {
                if (event.detail == SWT.TRAVERSE_ESCAPE) {

                    shell.close();

                }
            }
        });
    }

    /**
     * initializes the bottom part of the dialog
     * 
     * @param featuremodel
     * @param constraint
     */
    private void initBottom(final FeatureModel featuremodel, final Constraint constraint) {
        GridData gridData = new GridData(GridData.FILL_HORIZONTAL);

        Composite lastComposite = new Composite(shell, SWT.NONE);
        lastComposite.setLayoutData(gridData);

        FormLayout lastCompositeLayout = new FormLayout();
        lastCompositeLayout.marginHeight = 5;
        lastCompositeLayout.marginTop = 85;
        lastCompositeLayout.marginWidth = 5;
        lastComposite.setLayout(lastCompositeLayout);
        ToolBar helpButtonBar = new ToolBar(lastComposite, SWT.FLAT);
        ToolItem helpButton = new ToolItem(helpButtonBar, SWT.NONE);
        helpButton.setImage(HELP_IMAGE);
        helpButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
            public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
                Program.launch("http://www.cs.utexas.edu/~schwartz/ATS/fopdocs/guidsl.html");
            }
        });
        FormData formDataHelp = new FormData();
        formDataHelp.left = new FormAttachment(0, 5);
        helpButtonBar.setLayoutData(formDataHelp);

        Button cancelButton = new Button(lastComposite, SWT.NONE);
        cancelButton.setText("Cancel");
        FormData formDataCancel = new FormData();
        formDataCancel.width = 70;
        formDataCancel.right = new FormAttachment(100, -5);
        formDataCancel.bottom = new FormAttachment(100, -5);
        cancelButton.setLayoutData(formDataCancel);

        okButton = new Button(lastComposite, SWT.NONE);
        okButton.setText("OK");
        FormData formDataOk = new FormData();
        formDataOk.width = 70;
        formDataOk.right = new FormAttachment(cancelButton, -5);
        formDataOk.bottom = new FormAttachment(100, -5);
        okButton.setLayoutData(formDataOk);
        shell.setTabList(new Control[] { featureGroup, buttonGroup, constraintTextComposite, lastComposite });

        lastComposite.setTabList(new Control[] { okButton, cancelButton });
        okButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
            public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {

                closeShell();

            }

        });
        cancelButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
            public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {

                shell.dispose();
            }
        });

    }

    /**
     * initializes the upper part of the dialog
     */
    private void initHead() {
        Composite headComposite = new Composite(shell, SWT.NONE);
        headComposite.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
        headComposite.setLayoutData(gridData);

        GridLayout headLayout = new GridLayout();
        headLayout.numColumns = 3;
        headComposite.setLayout(headLayout);

        final Label capture = new Label(headComposite, SWT.NONE);
        FontData fontData = capture.getFont().getFontData()[0];
        Font font = new Font(shell.getDisplay(), new FontData(fontData.getName(), 11, SWT.NONE));
        capture.setFont(font);
        capture.setText("Constraint");
        capture.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WHITE));

        gridData = new GridData();
        gridData.horizontalSpan = 2;
        capture.setLayoutData(gridData);
        Label imageLabel = new Label(headComposite, SWT.RIGHT | SWT.DOWN);
        imageLabel.setImage(BANNER_IMAGE);
        imageLabel.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        gridData = new GridData(GridData.FILL_VERTICAL | GridData.END | GridData.HORIZONTAL_ALIGN_END
                | GridData.VERTICAL_ALIGN_END);
        gridData.widthHint = 90;
        gridData.verticalSpan = 3;
        imageLabel.setLayoutData(gridData);

        errorMarker = new Label(headComposite, SWT.NONE);
        gridData = new GridData(GridData.BEGINNING);
        gridData.widthHint = 20;
        gridData.heightHint = 20;
        gridData.verticalSpan = 2;
        errorMarker.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        errorMarker.setLayoutData(gridData);

        errorMessage = new Text(headComposite, SWT.MULTI);
        errorMessage.setEditable(false);
        errorMessage.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        gridData = new GridData(GridData.FILL_HORIZONTAL);
        gridData.grabExcessHorizontalSpace = true;
        gridData.grabExcessVerticalSpace = true;
        gridData.verticalSpan = 2;

        errorMessage.setLayoutData(gridData);
    }

    /**
     * initializes the Text containing the constraint
     */
    private void initConstraintText() {
        constraintTextComposite = new Composite(shell, SWT.NONE);
        GridData gridData = new GridData(GridData.FILL_HORIZONTAL);

        constraintTextComposite.setLayoutData(gridData);
        FormLayout constraintTextLayout = new FormLayout();
        constraintTextComposite.setLayout(constraintTextLayout);
        constraintText = new Text(constraintTextComposite, SWT.SINGLE | SWT.BORDER);

        ContentProposalAdapter adapter = new ContentProposalAdapter(constraintText, new ConstraintContentAdapter(),
                new ConstraintContentProposalProvider(featureModel.getFeatureNames()), null, null);

        adapter.setAutoActivationDelay(500);
        adapter.setPopupSize(new Point(250, 85));
        adapter.setLabelProvider(new ConstraintProposalLabelProvider());
        FormData formDataConstraintText = new FormData();
        formDataConstraintText.right = new FormAttachment(100, -5);
        formDataConstraintText.left = new FormAttachment(0, 5);
        constraintText.setLayoutData(formDataConstraintText);
        constraintText.setText(initialConstraint);
        constraintText.addListener(SWT.FocusOut, new Listener() {

            @Override
            public void handleEvent(Event event) {

                x = constraintText.getSelection().x;
                y = constraintText.getSelection().y;

            }

        });

        constraintText.addModifyListener(new ModifyListener() {

            @Override
            public void modifyText(ModifyEvent e) {
                validate();

            }

        });

    }

    /**
     * initializes the Group containing the operator buttons
     */
    private void initButtonGroup() {
        buttonGroup = new Group(shell, SWT.NONE);
        buttonGroup.setText("Operators");
        GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
        gridData.grabExcessHorizontalSpace = true;
        buttonGroup.setLayoutData(gridData);
        GridLayout buttonGroupLayout = new GridLayout();
        buttonGroupLayout.numColumns = 7;
        buttonGroup.setLayout(buttonGroupLayout);

        for (int i = 0; i < Operator.NAMES.length; i++) {

            final Button button = new Button(buttonGroup, SWT.PUSH);
            button.setText(Operator.NAMES[i]);
            gridData = new GridData(GridData.FILL_HORIZONTAL);
            button.setLayoutData(gridData);
            button.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
                public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
                    StringBuilder temp = new StringBuilder(constraintText.getText());
                    temp.delete(x, y);
                    temp.insert(x > y ? y : x, /*
                                               * " " +
                                               */button.getText().toLowerCase(Locale.ENGLISH) + " "

                    /* .replaceAll(" ", "") + " " */);
                    constraintText.setText(temp.toString()); //NodeReader.reduceWhiteSpaces(temp.toString()));
                    constraintText.setFocus();
                    constraintText.setSelection(constraintText.getCharCount());

                    validate();
                }
            });

        }

    }

    /**
     * initializes the group containing the searchText and featureTable
     * 
     * @param featuremodel
     */
    private void initFeatureGroup(final FeatureModel featuremodel) {

        featureGroup = new Group(shell, SWT.NONE);
        featureGroup.setText("Features");
        GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
        gridData.grabExcessHorizontalSpace = true;
        gridData.grabExcessVerticalSpace = true;
        featureGroup.setLayoutData(gridData);
        GridLayout featureGroupLayout = new GridLayout();
        featureGroupLayout.numColumns = 1;
        featureGroup.setLayout(featureGroupLayout);

        searchFeatureText = new StyledText(featureGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
        searchFeatureText.setText(FILTERTEXT);
        searchFeatureText.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_GRAY));
        gridData = new GridData(GridData.FILL_HORIZONTAL);
        searchFeatureText.setLayoutData(gridData);

        Composite tableComposite = new Composite(featureGroup, SWT.NONE);
        gridData = new GridData(GridData.FILL_HORIZONTAL);
        gridData.grabExcessHorizontalSpace = true;
        gridData.grabExcessVerticalSpace = true;
        tableComposite.setLayoutData(gridData);

        final TableViewer featureTableViewer = new TableViewer(tableComposite,
                SWT.BORDER | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);
        featureTable = featureTableViewer.getTable();
        featureTableViewer.setContentProvider(new ArrayContentProvider());
        TableViewerColumn viewerNameColumn = new TableViewerColumn(featureTableViewer, SWT.NONE);
        TableColumnLayout tableColumnLayout = new TableColumnLayout();
        tableComposite.setLayout(tableColumnLayout);
        tableColumnLayout.setColumnData(viewerNameColumn.getColumn(), new ColumnWeightData(100, 100, false));

        featureTableViewer.setComparator(new ViewerComparator() {

            @Override
            public int compare(Viewer viewer, Object feature1, Object feature2) {

                return ((Feature) feature1).getName().compareToIgnoreCase(((Feature) feature2).getName());
            }

        });

        viewerNameColumn.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(ViewerCell cell) {
                cell.setText(((Feature) cell.getElement()).getName());

            }
        });

        searchFeatureText.addModifyListener(new ModifyListener() {

            @Override
            public void modifyText(ModifyEvent e) {
                ViewerFilter searchFilter = new ViewerFilter() {

                    @Override
                    public boolean select(Viewer viewer, Object parentElement, Object element) {
                        return ((Feature) element).getName().toLowerCase(Locale.ENGLISH)
                                .contains(searchFeatureText.getText().toLowerCase(Locale.ENGLISH));
                    }

                };
                if (!searchFeatureText.getText().equalsIgnoreCase(FILTERTEXT)) {
                    featureTableViewer.addFilter(searchFilter);

                }
            }

        });

        searchFeatureText.addListener(SWT.FocusOut, new Listener() {

            @Override
            public void handleEvent(Event event) {
                if (searchFeatureText.getText().isEmpty()) {
                    searchFeatureText.setText(FILTERTEXT);
                    searchFeatureText.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_GRAY));

                }

            }
        });
        searchFeatureText.addListener(SWT.FocusIn, new Listener() {

            @Override
            public void handleEvent(Event event) {
                if (searchFeatureText.getText().equals(FILTERTEXT))

                    searchFeatureText.setText("");
                searchFeatureText.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_BLACK));
            }

        });

        featureTableViewer.setInput(featureModel.getFeatures());

        gridData = new GridData(GridData.FILL_HORIZONTAL);
        gridData.grabExcessVerticalSpace = true;
        featureTable.setLayoutData(gridData);

        featureTable.addListener(SWT.MouseDoubleClick, new Listener() {
            @Override
            public void handleEvent(Event event) {
                TableItem[] selectedItem = featureTable.getSelection();
                StringBuilder temp = new StringBuilder(constraintText.getText());

                temp.delete(x, y);
                if (selectedItem.length > 0) {
                    temp.insert(x > y ? y : x,
                            " " + (selectedItem[0].getText().contains(" ")
                                    || Operator.isOperatorName(selectedItem[0].getText())
                                            ? "\"" + selectedItem[0].getText() + "\""
                                            : selectedItem[0].getText())
                                    + " ");

                }
                constraintText.setText(temp.toString()); //NodeReader.reduceWhiteSpaces(temp.toString()));

                constraintText.setSelection(constraintText.getCharCount());
                searchFeatureText.setText(FILTERTEXT);
                searchFeatureText.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_GRAY));
                constraintText.setFocus();
                featureTableViewer.resetFilters();

                validate();
            }
        });

    }

    /**
     * returns true if constraint is satisfiable otherwise false
     * 
     * @param constraint
     *            the constraint to be evaluated
     * @param timeout
     *            timeout in ms
     */
    public boolean isSatisfiable(String constraint, int timeout) {
        NodeReader nodeReader = new NodeReader();
        SatSolver satsolver = new SatSolver(nodeReader.stringToNode(constraint).clone(), timeout);
        try {
            return satsolver.isSatisfiable();
        } catch (TimeoutException e) {
            FMUIPlugin.getDefault().logError(e);
            return true;
        }

    }

    /**
     * returns true if the constraint is always true
     * 
     * @param constraint
     *            the constraint to be evaluated
     * @param timeout
     *            timeout in ms
     * 
     */
    public boolean isTautology(String constraint, int timeout) {
        NodeReader nodeReader = new NodeReader();
        Node node = nodeReader.stringToNode(constraint);

        SatSolver satsolver = new SatSolver(new Not(node.clone()), timeout);

        try {
            return !satsolver.isSatisfiable();
        } catch (TimeoutException e) {

            return true;
        }

    }

    /**
     * returns true if the constraint causes the feature model to be void
     * otherwise false
     * 
     * @param input
     *            constraint to be evaluated
     * @param model
     *            the feature model
     * 
     *            * @throws TimeoutException
     */
    public boolean voidsModel(String input, FeatureModel model) throws TimeoutException {

        if (!model.getAnalyser().isValid()) {

            return false;
        }
        if (input.length() == 0) {

            return false;
        }
        FeatureModel clonedModel = model.clone();
        NodeReader nodeReader = new NodeReader();

        List<String> featureList = new ArrayList<String>(clonedModel.getFeatureNames());
        Node propNode = nodeReader.stringToNode(input, featureList);
        if (propNode != null) {
            if (constraint != null) {
                clonedModel.removePropositionalNode(constraint);
            }
            clonedModel.addPropositionalNode(propNode);
            clonedModel.handleModelDataChanged();
        }

        return (!clonedModel.getAnalyser().isValid());

    }

    /**
     * returns a List of all features that are caused to be dead by the
     * constraint input
     * 
     * @param input
     *            constraint to be evaluated
     * @param model
     *            the feature model
     * @return List of all dead Features, empty if no feature is caused to be
     *         dead
     */
    public List<Feature> getDeadFeatures(String input, FeatureModel model) {
        List<Feature> deadFeaturesBefore = null;
        FeatureModel clonedModel = model.clone();

        NodeReader nodeReader = new NodeReader();

        List<String> featureList = new ArrayList<String>(clonedModel.getFeatureNames());
        Node propNode = nodeReader.stringToNode(input, featureList);

        if (propNode != null) {
            if (constraint != null) {
                clonedModel.removePropositionalNode(constraint);
            }
            deadFeaturesBefore = clonedModel.getAnalyser().getDeadFeatures();
            clonedModel.addPropositionalNode(propNode);
            clonedModel.handleModelDataChanged();
        }

        List<Feature> deadFeaturesAfter = new ArrayList<Feature>();
        for (Feature l : clonedModel.getAnalyser().getDeadFeatures()) {
            if (!deadFeaturesBefore.contains(l)) {
                deadFeaturesAfter.add(l);

            }
        }
        return deadFeaturesAfter;
    }

    public List<Feature> getFalseOptional(String input, FeatureModel model) {
        List<Feature> list = new ArrayList<Feature>();
        FeatureModel clonedModel = model.clone();

        NodeReader nodeReader = new NodeReader();

        List<String> featureList = new ArrayList<String>(clonedModel.getFeatureNames());
        Node propNode = nodeReader.stringToNode(input, featureList);

        for (Feature feature : model.getFeatures()) {
            if (input.contains(feature.getName())) {
                if (feature.getFeatureStatus() != FeatureStatus.FALSE_OPTIONAL) {
                    clonedModel.addPropositionalNode(propNode);
                    clonedModel.getAnalyser().analyzeFeatureModel(null);
                    if (clonedModel.getFeature(feature.getName()).getFeatureStatus() == FeatureStatus.FALSE_OPTIONAL
                            && !list.contains(feature))
                        list.add(feature);
                }
            }
        }

        return list;
    }

    public String getFalseOptionalString(List<Feature> list) {
        String listString = list.toString();
        String featureString = "Constraint causes the following features to be false optional: " + '\n';
        return featureString + listString.substring(1, listString.length() - 1);
    }

    /**
     * validates the current constraint in constraintText
     * 
     */
    private boolean validate() {
        NodeReader nodereader = new NodeReader();
        String con = constraintText.getText().trim();
        boolean isWellformed = nodereader.isWellFormed(con, new ArrayList<String>(featureModel.getFeatureNames()));

        if (!isWellformed) {
            printHeaderError(nodereader.getErrorMessage());
            return false;
        }

        if (isTautology(con, 1000)) {

            printHeaderWarning("constraint is a tautology");
            return false;
        }
        if (!isSatisfiable(con, 1000)) {

            printHeaderWarning("constraint is unsatisfiable");
            return false;
        }
        try {
            if (featureModel.getAnalyser().isValid() && voidsModel(con, featureModel)) {

                printHeaderWarning("constraint makes model void");
                return false;
            }
        } catch (TimeoutException e) {
            FMUIPlugin.getDefault().logError(e);
        }

        List<Feature> deadFeatures = getDeadFeatures(con, featureModel);
        if (!deadFeatures.isEmpty()) {
            printHeaderWarning(getDeadFeatureString(deadFeatures));
            return false;
        }

        List<Feature> falseOptionalFeatures = getFalseOptional(con, featureModel);
        if (!falseOptionalFeatures.isEmpty()) {
            printHeaderWarning(getFalseOptionalString(falseOptionalFeatures));
            return false;
        }

        try {
            if (featureModel.getAnalyser().isValid() && redundant(con)) {
                printHeaderWarning("The constraint does not change the product line.");
                return true;
            }
        } catch (TimeoutException e) {
            FMUIPlugin.getDefault().logError(e);
        }

        printHeaderText(headerText);
        return true;
    }

    /**
     * Tests if the {@link Constraint} will change the product line. 
     * @param constraint The actual {@link Constraint}
     * @return <code>true</code> if the {@link Constraint} is redundant
     */
    private boolean redundant(String constraint) {
        if (constraint.length() == 0) {
            return false;
        }
        FeatureModel clonedModel = featureModel.clone();
        List<String> featureList = new ArrayList<String>(clonedModel.getFeatureNames());
        Node propNode = new NodeReader().stringToNode(constraint, featureList);
        clonedModel.addPropositionalNode(propNode);
        if (new ModelComparator(20000).compare(featureModel, clonedModel) == Comparison.REFACTORING) {
            return true;
        }
        return false;
    }

    /**
     * returns a String to be displayed in the dialog header contains the list
     * of dead features
     * 
     * @param deadFeatures
     *            List of dead Features
     * */
    private String getDeadFeatureString(List<Feature> deadFeatures) {
        StringBuilder featureString = new StringBuilder();
        featureString.append("Constraint causes the following features to be dead: ");
        int count = 0;
        int featureCount = 0;
        boolean isNewLine = false;
        for (Feature l : deadFeatures) {
            count = count + l.toString().length() + 2;

            if (isNewLine == false && count > 35) {
                featureString.append('\n');
                isNewLine = true;
            }
            if (count < 90) {
                featureString.append(l);
                if (featureCount < deadFeatures.size() - 1)
                    featureString.append(", ");
                featureCount++;

            }
            if (deadFeatures.indexOf(l) == deadFeatures.size() - 1) {

            }

        }
        if (featureCount < deadFeatures.size()) {
            featureString.append("...");
        }
        return featureString.toString();
    }

    /**
     * displays a warning in the header
     * 
     * @param message
     *            message to be displayed
     */
    private void printHeaderWarning(String message) {
        okButton.setEnabled(true);
        errorMarker.setImage(WARNING_IMAGE);
        errorMarker.setVisible(true);
        errorMessage.setText(message);
        errorMessage.pack();
    }

    /**
     * displays an error in the header
     * 
     * @param message
     *            message to be displayed
     */
    private void printHeaderError(String message) {
        okButton.setEnabled(false);
        errorMarker.setImage(ERROR_IMAGE);
        errorMarker.setVisible(true);
        errorMessage.setText(message);
    }

    /**
     * displays a Text in the header
     * 
     * @param message
     *            message to be displayed
     */
    private void printHeaderText(String message) {
        okButton.setEnabled(true);
        errorMarker.setVisible(false);
        errorMessage.setText(message);

    }

    /**
     * closes the shell and adds new constraint to the feature model if possible
     * 
     * @param featureModel
     * @param constraint
     */
    private void closeShell() {
        NodeReader nodeReader = new NodeReader();
        String input = constraintText.getText().trim();

        if (input.length() == 0) {
            printHeaderError("constraint is empty");
            return;
        }

        List<String> featureList = new ArrayList<String>(featureModel.getFeatureNames());
        Node propNode = nodeReader.stringToNode(input, featureList);

        if (propNode == null) {
            printHeaderError(nodeReader.getErrorMessage());
            return;
        }
        if (!isSatisfiable(input, 1000)) {
            printHeaderWarning("constraint is unsatisfiable");
        }

        int index = 0;
        AbstractOperation op = null;
        if (constraint != null && featureModel.getConstraints().contains(constraint)) {
            for (Constraint c : featureModel.getConstraints()) {
                if (c == constraint) {
                    op = new ConstraintEditOperation(propNode, featureModel, index);
                    break;
                }
                index++;
            }
        }
        if (op == null) {
            op = new ConstraintCreateOperation(propNode, featureModel);
        }
        op.addContext((IUndoContext) featureModel.getUndoContext());
        try {
            PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().execute(op, null, null);
        } catch (ExecutionException e) {
            FMUIPlugin.getDefault().logError(e);
        }

        shell.dispose();

    }
}