org.eclipse.emf.validation.ui.preferences.ConstraintsSelectionBlock.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.validation.ui.preferences.ConstraintsSelectionBlock.java

Source

/**
 * <copyright>
 *
 * Copyright (c) 2007 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 *
 * </copyright>
 *
 * $Id: ConstraintsSelectionBlock.java,v 1.5 2007/11/14 18:03:47 cdamus Exp $
 */
package org.eclipse.emf.validation.ui.preferences;

import java.text.MessageFormat;
import java.util.List;

import org.eclipse.emf.validation.model.Category;
import org.eclipse.emf.validation.preferences.EMFModelValidationPreferences;
import org.eclipse.emf.validation.service.IConstraintFilter;
import org.eclipse.emf.validation.service.ModelValidationService;
import org.eclipse.emf.validation.ui.internal.ValidationUIPlugin;
import org.eclipse.emf.validation.ui.internal.l10n.ValidationUIMessages;
import org.eclipse.emf.validation.ui.internal.preferences.CategoryTreeNode;
import org.eclipse.emf.validation.ui.internal.preferences.ConstraintDetailsHelper;
import org.eclipse.emf.validation.ui.internal.preferences.ConstraintNode;
import org.eclipse.emf.validation.ui.internal.preferences.ICategoryTreeNode;
import org.eclipse.emf.validation.ui.internal.preferences.IConstraintNode;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

/**
 * Encapsulation of the control for browsing and manipulating
 * constraints in the constraints preference page.
 * <p>
 * Clients can use this class to implement their own filtered
 * constraints preference page.
 * </p>
 * 
 * @since 1.1
 *
 * @author David Cummings (dcummin)
 * @author Christian W. Damus (cdamus)
 */
public class ConstraintsSelectionBlock {

    /** name of the lock icon file */
    private static final String LOCK_ICON = "full/lock.gif"; //$NON-NLS-1$

    static final String CATEGORIES_PROMPT = ValidationUIMessages.prefs_categories_prompt;
    static final String CONSTRAINTS_PROMPT = ValidationUIMessages.prefs_constraints_prompt;
    static final String NO_SELECTION = ValidationUIMessages.prefs_no_selection;
    static final String NO_CATEGORY_DESCRIPTION = ValidationUIMessages.prefs_no_description_category;

    private CheckboxTreeViewer categoryTree;
    private CheckboxTableViewer constraintList;
    private StyledText detailsArea;

    private Mediator mediator;

    private ICategoryTreeNode rootcategory;

    private final IConstraintFilter filter;

    static {
        // ensure that the statically declared (in XML) constraints are
        //   available for display to the user
        ModelValidationService.getInstance().loadXmlConstraintDeclarations();
    }

    /**
     * Initializes me without a constraint filter.
     */
    public ConstraintsSelectionBlock() {
        this(IConstraintFilter.IDENTITY_INSTANCE);
    }

    /**
     * Initializes me with a constraint filter.
     * 
     * @param filter used to filter the constraints which are presented in this 
     *        composite (must not be <code>null</code>)
     */
    public ConstraintsSelectionBlock(IConstraintFilter filter) {
        if (filter == null) {
            throw new IllegalArgumentException("null filter"); //$NON-NLS-1$
        }

        this.filter = filter;
    }

    /**
     * Content provider for the category tree.
     * 
     * @author Christian W. Damus (cdamus)
     */
    private class CategoryTreeContents implements ITreeContentProvider {

        // implements the inherited method
        public Object[] getChildren(Object parentElement) {
            return ((ICategoryTreeNode) parentElement).getChildren();
        }

        // implements the inherited method
        public Object getParent(Object element) {
            return ((ICategoryTreeNode) element).getParent();
        }

        // implements the inherited method
        public boolean hasChildren(Object element) {
            return ((ICategoryTreeNode) element).hasChildren();
        }

        // implements the inherited method
        public Object[] getElements(Object inputElement) {
            return getChildren(inputElement);
        }

        // implements the inherited method
        public void dispose() {
            // no cached resources to dispose
        }

        // implements the inherited method
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            // nothing to do
        }
    }

    /**
     * Content provider for the constraints list.
     * 
     * @author Christian W. Damus (cdamus)
     */
    private class ConstraintListContents implements IStructuredContentProvider, ICheckStateListener {

        private CheckboxTableViewer viewer;
        private ICategoryTreeNode category;

        public Object[] getElements(Object inputElement) {
            if (inputElement == null) {
                return new Object[0];
            } else {
                category = (ICategoryTreeNode) inputElement;

                return category.getConstraints().toArray();
            }
        }

        public void dispose() {
            // nothing to dispose
        }

        public void inputChanged(Viewer newViewer, Object oldInput, Object newInput) {
            if (viewer != null) {
                viewer.removeCheckStateListener(this);
            }

            viewer = (CheckboxTableViewer) newViewer;
            category = (ICategoryTreeNode) newInput;

            if (viewer != null) {
                viewer.addCheckStateListener(this);
            }
        }

        /* (non-Javadoc)
         * Redefines/Implements/Extends the inherited method.
         */
        public void checkStateChanged(CheckStateChangedEvent event) {
            category.updateCheckState((IConstraintNode) event.getElement());
        }
    }

    /**
     * A <i>Mediator</i> to coordinate the category tree, constraints list, and
     * details pane in the GUI.
     * 
     * @author Christian W. Damus (cdamus)
     */
    private class Mediator implements ISelectionChangedListener, ICheckStateListener {

        private boolean respondingToUserSelection;

        // implements the interface method
        public void checkStateChanged(CheckStateChangedEvent event) {
            Object element = event.getElement();

            if (element instanceof ICategoryTreeNode) {
                ICategoryTreeNode node = (ICategoryTreeNode) element;

                if (!respondingToUserSelection) {
                    respondingToUserSelection = true;

                    try {
                        node.checkStateChanged(event);

                        // update the constraint selections of the currently
                        // selected category (because the one that changed
                        // might be an ancestory
                        IStructuredSelection selection = (IStructuredSelection) getCategoryTree().getSelection();

                        if (!selection.isEmpty()) {
                            selectCategory((ICategoryTreeNode) selection.getFirstElement());
                        }
                    } finally {
                        respondingToUserSelection = false;
                    }
                }
            } else {
                IConstraintNode node = (IConstraintNode) element;

                if (!respondingToUserSelection) {
                    respondingToUserSelection = true;

                    try {
                        node.checkStateChanged(event);
                    } finally {
                        respondingToUserSelection = false;
                    }
                }
            }
        }

        // implements the interface method
        public void selectionChanged(SelectionChangedEvent event) {
            IStructuredSelection selection = (IStructuredSelection) event.getSelection();

            if (event.getSource().equals(getCategoryTree())) {
                handleCategorySelection(selection);
            } else if (event.getSource().equals(getConstraintList())) {
                handleConstraintSelection(selection);
            }
        }

        /**
         * Handles a selection change in the category tree.
         * 
         * @param selection the new selection
         */
        private void handleCategorySelection(IStructuredSelection selection) {
            if (!selection.isEmpty()) {
                selectCategory((ICategoryTreeNode) selection.getFirstElement());
            } else {
                getConstraintList().setInput(null);
                clearDetailsArea();
            }
        }

        /**
         * Selects the specified category in the constraints list.
         * 
         * @param category the category to select
         */
        private void selectCategory(ICategoryTreeNode category) {
            getConstraintList().setInput(category);
            selectConstraints(category);
            setDetails(category);
        }

        /**
         * Select, in the table viewer, the currently enabled constraints.
         * 
         * @param categoryNode the currently selected category node
         */
        private void selectConstraints(ICategoryTreeNode categoryNode) {
            getConstraintList().setCheckedElements(categoryNode.getSelectedConstraints());
        }

        /**
         * Handles a selection change in the constraints list.
         * 
         * @param selection the new selection
         */
        private void handleConstraintSelection(IStructuredSelection selection) {
            if (!selection.isEmpty()) {
                setDetails((IConstraintNode) selection.getFirstElement());
            } else {
                clearDetailsArea();
            }
        }

        /**
         * Clears my details area, showing the "no selection" message.
         */
        void clearDetailsArea() {
            getDetailsArea().setText(NO_SELECTION);
        }

        /**
         * Sets the details area to show the currently selected
         * <code>category</code>'s category details.
         * 
         * @param category the category in the category tree
         */
        private void setDetails(ICategoryTreeNode category) {
            String description = (category == null) ? null : category.getDescription();

            if (description == null) {
                description = NO_CATEGORY_DESCRIPTION;
            }

            Category actualCategory = category.getCategory();
            // If we are a mandatory category then we must provide some cue to this fact.
            if (actualCategory != null && actualCategory.isMandatory()) {
                getDetailsArea().setText(MessageFormat.format(ValidationUIMessages.prefs_mandatory_category,
                        new Object[] { description }));

            } else {
                getDetailsArea().setText(description);
            }
        }

        /**
         * Sets the details area to show the currently selected
         * <code>constraint</code>'s details.
         * 
         * @param constraint the constraint meta-data
         */
        private void setDetails(IConstraintNode constraint) {
            // lots of style info
            List<StyleRange> styles = new java.util.ArrayList<StyleRange>(32);
            String text = ConstraintDetailsHelper.formatConstraintDescription(constraint,
                    getCurrentCategorySelection(), styles);

            getDetailsArea().setText(text);
            getDetailsArea().setStyleRanges(styles.toArray(new StyleRange[styles.size()]));
        }
    }

    /**
      * Creates the constraints selection composite on the given parent and filters
      * the composite based on the provided filter.
      *
      * @param parent parent for the newly created composite
      * @return the resulting constraint selection composite
      */
    public Composite createComposite(Composite parent) {
        SashForm result = new SashForm(parent, SWT.VERTICAL);

        result.setFont(parent.getFont());

        SashForm topPart = new SashForm(result, SWT.HORIZONTAL);
        createCategoryTree(topPart);
        createConstraintList(topPart);

        createDetailsArea(result);

        result.setWeights(new int[] { 70, 30 });

        return result;
    }

    /**
     * Helper method to create the category-tree part of the GUI.  The result
     * is a form containing the checkbox tree and a prompt label.
     * 
     * @param parent the parent composite in which to create the tree
     * @return the tree part of the GUI (itself a composite form)
     */
    private Control createCategoryTree(Composite parent) {
        Composite form = new Composite(parent, SWT.NONE);
        FormLayout layout = new FormLayout();
        form.setLayout(layout);

        Label prompt = new Label(form, SWT.NONE);
        prompt.setText(CATEGORIES_PROMPT);
        FormData data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        prompt.setLayoutData(data);

        categoryTree = new CheckboxTreeViewer(form);
        data = new FormData();
        data.top = new FormAttachment(prompt, 4);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        data.bottom = new FormAttachment(100, 0);
        categoryTree.getControl().setLayoutData(data);

        rootcategory = CategoryTreeNode.createRoot(categoryTree, filter);
        categoryTree.setLabelProvider(new LabelProvider() {
            private final Image lockImage = ValidationUIPlugin.getImageDescriptor(LOCK_ICON).createImage(true);

            @Override
            public void dispose() {
                lockImage.dispose();
                super.dispose();
            }

            @Override
            public Image getImage(Object element) {
                ICategoryTreeNode node = (ICategoryTreeNode) element;

                if (node.getCategory() != null) {
                    if (node.getCategory().isMandatory()) {
                        return lockImage;
                    }
                }

                return null;
            }
        });
        categoryTree.setContentProvider(new CategoryTreeContents());
        categoryTree.setInput(rootcategory);
        markEnabledCategories(rootcategory);

        categoryTree.addCheckStateListener(getMediator());
        categoryTree.addSelectionChangedListener(getMediator());

        return categoryTree.getTree();
    }

    /**
     * Helper method to create the constraint-list part of the GUI.  The result
     * is a form containing the constraints list and a prompt label.
     * 
     * @param parent the parent composite in which to create the list
     * @return the list part of the GUI (itself a composite form)
     */
    private Control createConstraintList(Composite parent) {
        Composite form = new Composite(parent, SWT.NONE);
        FormLayout layout = new FormLayout();
        form.setLayout(layout);

        Label prompt = new Label(form, SWT.NONE);
        prompt.setText(CONSTRAINTS_PROMPT);
        FormData data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        prompt.setLayoutData(data);

        constraintList = CheckboxTableViewer.newCheckList(form, SWT.CHECK | SWT.BORDER);
        data = new FormData();
        data.top = new FormAttachment(prompt, 4);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        data.bottom = new FormAttachment(100, 0);
        constraintList.getControl().setLayoutData(data);

        constraintList.setContentProvider(new ConstraintListContents());

        constraintList.setLabelProvider(new LabelProvider() {
            private final Image lockImage = ValidationUIPlugin.getImageDescriptor(LOCK_ICON).createImage(true);

            // extends the inherited method
            @Override
            public void dispose() {
                lockImage.dispose();
                super.dispose();
            }

            // redefines the inherited method
            @Override
            public Image getImage(Object element) {
                IConstraintNode constraint = (IConstraintNode) element;

                if (constraint.isErrored()) {
                    return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
                } else if (constraint.isMandatory()) {
                    return lockImage;
                } else {
                    return null;
                }
            }

            // redefines the inherited method
            @Override
            public String getText(Object element) {
                return ((IConstraintNode) element).getName();
            }
        });

        constraintList.setSorter(new ViewerSorter());

        constraintList.addCheckStateListener(getMediator());
        constraintList.addSelectionChangedListener(getMediator());

        return constraintList.getControl();
    }

    /**
     * Helper method to create the details are of the GUI.
     * 
     * @param parent the parent composite in which to create the details area
     * @return the details text area
     */
    private Control createDetailsArea(Composite parent) {
        detailsArea = new StyledText(parent, SWT.READ_ONLY | SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);

        getMediator().clearDetailsArea();

        return detailsArea;
    }

    /**
     * Obtains my category tree.
     * 
     * @return the tree
     */
    private CheckboxTreeViewer getCategoryTree() {
        return categoryTree;
    }

    /**
     * Obtains my constraints list.
     * 
     * @return the list
     */
    private CheckboxTableViewer getConstraintList() {
        return constraintList;
    }

    /**
     * Obtains my details area.
     * 
     * @return the details text area
     */
    private StyledText getDetailsArea() {
        return detailsArea;
    }

    /**
     * Obtains my mediator.
     * 
     * @return my mediator
     */
    private Mediator getMediator() {
        if (mediator == null) {
            mediator = new Mediator();
        }

        return mediator;
    }

    /**
      * Saves the constraint enablement changes made in the composite 
      * to the validation preferences
      */
    public boolean performOk() {
        rootcategory.applyToPreferences();
        EMFModelValidationPreferences.save();

        return true;
    }

    /**
      * Restores the defaults for the constraints listed in the composite
      */
    public void performDefaults() {
        rootcategory.restoreDefaults();

        // update the checked state of the current contents of the Constraints
        // list (if any)
        CheckboxTableViewer viewer = getConstraintList();
        Object input = viewer.getInput();

        if (input != null) {
            Object[] elements = ((IStructuredContentProvider) getConstraintList().getContentProvider())
                    .getElements(input);

            if (elements != null) {
                int length = elements.length;

                for (int i = 0; i < length; i++) {
                    IConstraintNode node = (IConstraintNode) elements[i];
                    viewer.setChecked(node, node.isChecked());
                }
            }
        }
    }

    /**
     * Obtains the currently selected category, if any.
     * 
     * @return the current category
     */
    private Category getCurrentCategorySelection() {
        IStructuredSelection selection = (IStructuredSelection) getCategoryTree().getSelection();

        if (selection.isEmpty()) {
            return null;
        } else {
            return ((ICategoryTreeNode) selection.getFirstElement()).getCategory();
        }
    }

    /**
     * Helper method to set the currently enabled categories in the tree.
     * Also sets gray states as appropriate.
     * 
     * @param root the root of the tree model
     */
    private void markEnabledCategories(ICategoryTreeNode root) {
        markEnabledCategories(root.getChildren());
    }

    private void markEnabledCategories(ICategoryTreeNode[] categories) {
        for (ICategoryTreeNode next : categories) {
            getCategoryTree().setChecked(next, next.isChecked());
            getCategoryTree().setGrayed(next, next.isGrayed());

            markEnabledCategories(next.getChildren());
        }
    }

    /* (non-Javadoc)
     * Extends the inherited method.
     */
    public void dispose() {
        // clean up the cached constraint nodes
        ConstraintNode.flushCache();
    }
}