org.eclipse.wst.ws.internal.service.policy.ui.ServicePoliciesComposite.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wst.ws.internal.service.policy.ui.ServicePoliciesComposite.java

Source

/*******************************************************************************
 * Copyright (c) 2007, 2012 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 Corporation - initial API and implementation
 * yyyymmdd bug      Email and other contact information
 * -------- -------- -----------------------------------------------------------
 * 20071106 196997   ericdp@ca.ibm.com - Eric Peters
 * 20071120 209858 ericdp@ca.ibm.com - Eric Peters, Enhancing service policy framework and UI 
 * 20071212   209858 ericdp@ca.ibm.com - Eric Peters, Enhancing service policy framework and UI
 * 20080211   218520 pmoogk@ca.ibm.com - Peter Moogk
 * 20080324   222095 pmoogk@ca.ibm.com - Peter Moogk, UI now listens for state changes.
 * 20080324   223634 ericdp@ca.ibm.com - Eric D. Peters, Service Policies preference tree should be 3 level tree
 * 20080506   219005 ericdp@ca.ibm.com - Eric D. Peters, Service policy preference page not restoring properly
 * 20080530   234944 pmoogk@ca.ibm.com - Peter Moogk, Fixed focus problem for action controls
 * 20080716   239457 ericdp@ca.ibm.com - Eric D. Peters, Service Policy UI Restore defaults does not refresh validation error
 * 20090306   224632 ericdp@ca.ibm.com - Eric D. Peters, Fix Service Policies preference page UI
 * 20120111   357580 kchong@ca.ibm.com - Keith Chong, Service Policies preference page tree view pane doesn't expand to show fully the tree items
 *******************************************************************************/
package org.eclipse.wst.ws.internal.service.policy.ui;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.eclipse.wst.ws.service.policy.IDescriptor;
import org.eclipse.wst.ws.service.policy.IPolicyEnumerationList;
import org.eclipse.wst.ws.service.policy.IPolicyRelationship;
import org.eclipse.wst.ws.service.policy.IPolicyState;
import org.eclipse.wst.ws.service.policy.IPolicyStateEnum;
import org.eclipse.wst.ws.service.policy.IServicePolicy;
import org.eclipse.wst.ws.service.policy.IStateEnumerationItem;
import org.eclipse.wst.ws.service.policy.ServicePolicyPlatform;
import org.eclipse.wst.ws.service.policy.listeners.IPolicyChildChangeListener;
import org.eclipse.wst.ws.service.policy.listeners.IPolicyStateChangeListener;
import org.eclipse.wst.ws.service.policy.ui.IPolicyOperation;
import org.eclipse.wst.ws.service.policy.ui.ServicePolicyActivatorUI;
import org.eclipse.wst.ws.service.policy.ui.ServicePolicyPlatformUI;
import org.eclipse.wst.ws.service.policy.ui.IPolicyOperation.OperationKind;
import org.eclipse.wst.ws.service.policy.ui.utils.ServiceUtils;
import org.osgi.framework.Bundle;

public class ServicePoliciesComposite extends Composite implements SelectionListener, IPolicyChildChangeListener {

    private ScrolledComposite operationsScrolledComposite;
    //a scrollable composite containing operations available for the selected policies
    private Composite operationsComposite;
    private IWorkbenchHelpSystem helpSystem;
    private Composite masterComposite;
    private Composite detailsComposite;
    private Tree masterPolicyTree;
    //tertiary and higher level policy nodes in a separate tree
    private Tree detailsPolicyTree;
    private Text text_DetailsPanel_description;
    private Text text_DetailsPanel_dependencies;
    private Label label_DetailsPanel_description;
    private Label label_detailsPanel_dependancies;
    private Hashtable<String, IStatus> allErrors;
    private IStatus error;
    private boolean bComplexOpCompleted = true;
    private List<ChildChangeEvent> listChildChangeEvents;
    private IConManager iconManager = new IConManager();
    private IProject project = null;
    private SelectionListener listener;
    private ExpandableComposite excomposite;
    private ServicePolicyPlatform platform = ServicePolicyPlatform.getInstance();
    private ServicePolicyPlatformUI platformUI = ServicePolicyPlatformUI.getInstance();
    private IPolicyStateChangeListener stateChangeListener = new StateChangeListener();
    private boolean stateChangeEnabled = true;
    private HashSet<IServicePolicy> stateChangePolicySet = new HashSet<IServicePolicy>();
    private List<IServicePolicy> lastSelectedSp = null;
    /*CONTEXT_ID PTPP0001 for the Service Policies Preference & Property Page*/
    private String INFOPOP_SPPP_PAGE = ServicePolicyActivatorUI.PLUGIN_ID + ".SPPP0001";

    /**
     * Creates an expandable composite
     * @param parent
     * @param nColumns
     * @return
     */
    private ExpandableComposite createExpandableComposite(Composite parent, int nColumns) {
        ExpandableComposite excomposite = new ExpandableComposite(parent, SWT.NONE,
                ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);
        excomposite.setExpanded(false);
        excomposite.setVisible(false);
        excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
        excomposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, nColumns, 1));
        excomposite.addExpansionListener(new ExpansionAdapter() {
            public void expansionStateChanged(ExpansionEvent e) {
                expandedStateChanged((ExpandableComposite) e.getSource());
            }
        });
        makeScrollableCompositeAware(excomposite);
        return excomposite;
    }

    /**
     * @author ericdp
     * Helper class that encapsulates data required when an action control changes state
     *
     */
    private class ActionControlData {
        private List<IServicePolicy> spList;
        private List<IPolicyOperation> poList;
        //      private IPolicyOperation po;

        public ActionControlData(List<IServicePolicy> spList, List<IPolicyOperation> poList) {
            this.spList = spList;
            this.poList = poList;
        }

        public List<IServicePolicy> getSpList() {
            return spList;
        }

        public void setSpList(List<IServicePolicy> spList) {
            this.spList = spList;
        }
        //
        //      public IPolicyOperation getPo() {
        //         return po;
        //      }
        //
        //      public void setPo(IPolicyOperation po) {
        //         this.po = po;
        //      }

        public List<IPolicyOperation> getPoList() {
            return poList;
        }

        public void setPoList(List<IPolicyOperation> poList) {
            this.poList = poList;
        }
    }

    /**
     * Makes the scrollable composite aware of this control, so expand/collapse
     * of the scrollable composite will move this control down/up accordingly
     * @param control the control to make the scrollable composite aware of
     */
    private void makeScrollableCompositeAware(Control control) {
        ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(control);
        if (parentScrolledComposite != null) {
            parentScrolledComposite.adaptChild(control);
        }
    }

    private ScrolledPageContent getParentScrolledComposite(Control control) {
        Control parent = control.getParent();
        while (!(parent instanceof ScrolledPageContent) && parent != null) {
            parent = parent.getParent();
        }
        if (parent instanceof ScrolledPageContent) {
            return (ScrolledPageContent) parent;
        }
        return null;
    }

    private final void expandedStateChanged(ExpandableComposite expandable) {
        ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(expandable);
        if (parentScrolledComposite != null) {
            parentScrolledComposite.reflow(true);
        }
    }

    /**
     * @param parent
     * @return a scrollable composite containing an expandable content
     *
     */
    private Composite createDetailsScrollPageContent(Composite parent) {

        int nColumns = 1;

        final ScrolledPageContent pageContent = new ScrolledPageContent(parent);

        GridLayout pageContLayout = new GridLayout();
        pageContLayout.numColumns = nColumns;
        pageContLayout.marginHeight = 0;
        pageContLayout.marginWidth = 0;

        Composite composite = pageContent.getBody();
        composite.setLayout(pageContLayout);

        excomposite = createExpandableComposite(composite, nColumns);

        Composite inner = new Composite(excomposite, SWT.NONE);
        inner.setFont(composite.getFont());
        inner.setLayout(new GridLayout(nColumns, false));
        excomposite.setClient(inner);
        //details policy tree for tertiary and higher level service policies
        detailsPolicyTree = new Tree(inner, SWT.BORDER | SWT.MULTI);
        detailsPolicyTree.addSelectionListener(this);
        GridData detailsPrefTreeGD = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
        detailsPrefTreeGD.heightHint = 50;
        detailsPolicyTree.setLayoutData(detailsPrefTreeGD);
        detailsPolicyTree.setToolTipText(WstSPUIPluginMessages.TOOLTIP_PSP_DETAILSTREE);
        //make the scrollable composite aware of the tree so expand/collapse works properly
        makeScrollableCompositeAware(detailsPolicyTree);

        createPolicyOperationsComposite(composite);
        Composite descriptionAndDetailsComposite = new Composite(composite, SWT.NONE);
        descriptionAndDetailsComposite.setFont(composite.getFont());
        GridLayout layout = new GridLayout(nColumns, false);
        layout.marginHeight = 0;
        layout.marginWidth = 0;

        descriptionAndDetailsComposite.setLayout(layout);
        GridData detailsGD4 = new GridData(SWT.FILL, SWT.END, true, true);

        descriptionAndDetailsComposite.setLayoutData(detailsGD4);
        makeScrollableCompositeAware(descriptionAndDetailsComposite);
        label_DetailsPanel_description = new Label(descriptionAndDetailsComposite, SWT.NONE);
        GridData detailsGD2 = new GridData(SWT.FILL, SWT.END, true, true);
        label_DetailsPanel_description.setLayoutData(detailsGD2);
        label_DetailsPanel_description.setText(WstSPUIPluginMessages.LABEL_SERVICEPOLICIES_DESCRIPTION);
        makeScrollableCompositeAware(label_DetailsPanel_description);
        text_DetailsPanel_description = new Text(descriptionAndDetailsComposite,
                SWT.WRAP | SWT.BORDER | SWT.V_SCROLL | SWT.READ_ONLY);
        GridData detailsGD = new GridData(SWT.FILL, SWT.END, true, true);
        detailsGD.heightHint = 75;
        detailsGD.widthHint = 220;
        text_DetailsPanel_description.setLayoutData(detailsGD);
        text_DetailsPanel_description.setToolTipText(WstSPUIPluginMessages.TOOLTIP_PSP_DESCRIPTION);

        makeScrollableCompositeAware(text_DetailsPanel_description);
        label_detailsPanel_dependancies = new Label(descriptionAndDetailsComposite, SWT.NONE);
        GridData detailsGD3 = new GridData(SWT.FILL, SWT.END, true, true);
        label_detailsPanel_dependancies.setLayoutData(detailsGD3);
        makeScrollableCompositeAware(label_detailsPanel_dependancies);
        label_detailsPanel_dependancies.setText(WstSPUIPluginMessages.LABEL_SERVICEPOLICIES_DEPENDENCIES);
        text_DetailsPanel_dependencies = new Text(descriptionAndDetailsComposite,
                SWT.WRAP | SWT.BORDER | SWT.V_SCROLL | SWT.READ_ONLY);
        text_DetailsPanel_dependencies.setToolTipText(WstSPUIPluginMessages.TOOLTIP_PSP_DEPENDENCIES);
        makeScrollableCompositeAware(text_DetailsPanel_dependencies);

        composite.setLayout(pageContLayout);
        GridData dependenciesGD = new GridData(SWT.FILL, SWT.END, true, true);
        dependenciesGD.heightHint = 75;
        dependenciesGD.widthHint = 220;
        text_DetailsPanel_dependencies.setLayoutData(dependenciesGD);

        return pageContent;
    }

    /**
     * Creates the scrollable composite that will contain widgets associated with a policy operation
     * @param parent the parent composite
     */
    private void createPolicyOperationsComposite(Composite parent) {
        operationsScrolledComposite = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
        operationsScrolledComposite.setExpandHorizontal(true);
        operationsScrolledComposite.setExpandVertical(true);
        GridData operationsScrolledCompositeGD = new GridData(
                GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
        operationsScrolledCompositeGD.heightHint = 75;
        operationsScrolledComposite.setLayoutData(operationsScrolledCompositeGD);
        operationsComposite = new Composite(operationsScrolledComposite, SWT.NONE);
        operationsScrolledComposite.setContent(operationsComposite);
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;

        operationsComposite.setLayout(layout);
        operationsScrolledComposite.setMinSize(operationsComposite.computeSize(400, 100));
        makeScrollableCompositeAware(operationsScrolledComposite);
        makeScrollableCompositeAware(operationsComposite);
    }

    public ServicePoliciesComposite(Composite parent, IProject project, SelectionListener listener) {

        super(parent, SWT.NONE);
        this.project = project;
        this.listener = listener;
        allErrors = new Hashtable<String, IStatus>();
        helpSystem = PlatformUI.getWorkbench().getHelpSystem();
        GridLayout parentLayout = new GridLayout();
        parentLayout.numColumns = 2;
        parentLayout.horizontalSpacing = 0;

        this.setLayout(parentLayout);
        this.setLayoutData(new GridData(GridData.FILL_BOTH));
        Label description = new Label(this, SWT.NONE);
        description.setText(WstSPUIPluginMessages.LABEL_SERVICEPOLICIES_PAGE_DESCRIPTION);
        GridData descGD = new GridData(SWT.BEGINNING, SWT.TOP, false, false, 2, 1);
        description.setLayoutData(descGD);
        masterComposite = new Composite(this, SWT.NONE);

        GridLayout masterLayout = new GridLayout();
        masterLayout.numColumns = 1;
        masterLayout.horizontalSpacing = 0;
        masterComposite.setLayout(masterLayout);
        masterComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

        detailsComposite = new Composite(this, SWT.NONE);
        GridLayout detailsLayout = new GridLayout();
        detailsLayout.numColumns = 1;
        detailsLayout.horizontalSpacing = 0;
        detailsComposite.setLayout(detailsLayout);
        detailsComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
        masterPolicyTree = new Tree(masterComposite, SWT.BORDER | SWT.MULTI);
        masterPolicyTree.setLayoutData(new GridData(GridData.FILL_BOTH));
        masterPolicyTree.setToolTipText(WstSPUIPluginMessages.TOOLTIP_PSP_TREE);

        Composite othersComposite = createDetailsScrollPageContent(detailsComposite);
        GridData gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
        othersComposite.setLayoutData(gridData);

        masterPolicyTree.addSelectionListener(this);

        List<IServicePolicy> policyList = platform.getRootServicePolicies(null);

        policyList = ServiceUtils.sortList(policyList);

        for (IServicePolicy policy : policyList) {
            addPolicy(policy, masterPolicyTree, true);
        }
        TreeItem[] treeItems = masterPolicyTree.getItems();
        if (treeItems.length > 0) {
            //select the first item in the tree & fire event so UI is updated
            masterPolicyTree.setSelection(treeItems[0]);
            masterPolicyTree.notifyListeners(SWT.Selection, new Event());
        }
    }

    /**
     * Sets the image on ti, if associated service policy defines an icon
     * it will will be used, otherwise default Folder/Leaf icons will be used
     * @param ti the TreeItem to set the image for
     * @param sp the service policy associated with ti
     * @param iconOverlays description of which overlays should be applied 
     */
    private void setImage(TreeItem ti, IServicePolicy sp, String[] iconOverlays) {
        Image i = (sp.getChildren().size() == 0)
                ? iconManager.getIconOverlay(iconManager.getLeafBaseUrl(), iconOverlays)
                : iconManager.getIconOverlay(iconManager.getFolderBaseUrl(), iconOverlays);
        if (sp.getDescriptor() != null) {
            String iconPathBundleID = null;
            String iconPath = null;
            iconPathBundleID = sp.getDescriptor().getIconBundleId();
            iconPath = sp.getDescriptor().getIconPath();
            if (iconPathBundleID != null && iconPath != null && iconPathBundleID.length() > 0
                    && iconPath.length() > 0) {
                Bundle b = Platform.getBundle(iconPathBundleID);
                i = iconManager.getIconOverlay(FileLocator.find(b, new Path(iconPath), null).toString(),
                        iconOverlays);
            }
        }
        ti.setImage(i);
        // image has changed, notify change listeners so tree gets updated
        ti.notifyListeners(SWT.Modify, new Event());
    }

    private boolean is4thLevelOrHigherPolicy(IServicePolicy sp) {
        return sp.getParentPolicy() != null && sp.getParentPolicy().getParentPolicy() != null
                && sp.getParentPolicy().getParentPolicy().getParentPolicy() != null;

    }

    private boolean is3rdLevelPolInMasterTreeWithChildren(List<IServicePolicy> spListInMasterTree) {
        return spListInMasterTree.get(0).getParentPolicy() != null
                && spListInMasterTree.get(0).getParentPolicy().getParentPolicy() != null
                && spListInMasterTree.get(0).getChildren().size() > 0;
    }

    /**
     * Add service policy sp and children of sp to the parent
     * @param sp the service policy to add
     * @param parent either a Tree (masterPolicyTree or DetailsPolicyTree), or a TreeItem
     * to add service policy children to
     * @param addUpToLevel3Only false if should add 4th and higher level
     * child policies
     */
    private void addPolicy(IServicePolicy sp, Widget parent, boolean addUpToLevel3Only) {
        sp.addPolicyChildChangeListener(this);
        addStateListener(sp);
        if (addUpToLevel3Only && is4thLevelOrHigherPolicy(sp))
            // don't add 4th level and higher branches, these are added on demand
            // to a different tree
            return;
        TreeItem ti;
        if (parent instanceof TreeItem)
            ti = new TreeItem((TreeItem) parent, SWT.NONE);
        else
            ti = new TreeItem((Tree) parent, SWT.NONE);
        ti.setText(sp.getDescriptor().getLongName());
        ti.setData(sp);

        setImage(ti, sp, getIconOverlayInfo(sp, false));
        List<IServicePolicy> childrenPolicyList = sp.getChildren();

        childrenPolicyList = ServiceUtils.sortList(childrenPolicyList);

        for (IServicePolicy policy : childrenPolicyList) {
            addPolicy(policy, ti, addUpToLevel3Only);
        }

    }

    /**
     * Returns icon overlay information
     * @param sp the service policy that should have some overlay images associated with it
     * @param invalid true if the service policy is valid
     * @return String[] containing information about what overlays to apply to the icon that
     * is associated with sp
     */
    private String[] getIconOverlayInfo(IServicePolicy sp, boolean invalid) {
        String[] overLays = new String[4];

        IPolicyState polState = (project == null) ? sp.getPolicyState() : sp.getPolicyState(project);
        IPolicyStateEnum polEnum = (project == null) ? sp.getPolicyStateEnum() : sp.getPolicyStateEnum(project);
        if (!polState.isMutable())
            overLays[0] = iconManager.lock;
        if ((sp.getStatus() != null && sp.getStatus().getSeverity() == IStatus.ERROR) || invalid)
            overLays[1] = iconManager.invalid;
        if ((sp.getStatus() != null && sp.getStatus().getSeverity() == IStatus.WARNING))
            overLays[2] = iconManager.warning;
        if (polEnum != null) {
            if (polEnum.getEnumId().equals("org.eclipse.wst.service.policy.booleanEnum")) { //$NON-NLS-1$
                if (policyHasIconSelectionOperationSelected(sp))
                    overLays[3] = iconManager.favorite;
            }

        }
        return overLays;
    }

    /**
     * @param sp
     */
    private boolean policyHasIconSelectionOperationSelected(IServicePolicy sp) {
        boolean iconSelectionOperationSelected = false;
        List<IPolicyOperation> operationList = platformUI.getOperations(sp, project == null);
        for (IPolicyOperation operationItem : operationList) {
            if (operationItem.getOperationKind().equals(OperationKind.iconSelection)) {
                String stateID = operationItem.getStateItem(project);
                if (stateID.equals("org.eclipse.wst.true")) //$NON-NLS-1$
                    return true;
            }

        }
        return iconSelectionOperationSelected;
    }

    /**
     * Does anything necessary because the default button has been pressed.
     */
    public void performDefaults() {
        initializeDefaults();
        List<IServicePolicy> policyList = platform.getRootServicePolicies(null);

        policyList = ServiceUtils.sortList(policyList);
        masterPolicyTree.removeAll();

        for (IServicePolicy policy : policyList) {
            addPolicy(policy, masterPolicyTree, true);
        }
        TreeItem[] treeItems = masterPolicyTree.getItems();
        if (treeItems.length > 0) {
            //select the first item in the tree & fire event so UI is updated
            masterPolicyTree.setSelection(treeItems[0]);
            masterPolicyTree.notifyListeners(SWT.Selection, new Event());
            error = validateAllPolicies((IServicePolicy) treeItems[0].getData());
        }

    }

    private class ChildChangeEvent {
        public ChildChangeEvent(List<IServicePolicy> changedChildren, List<Boolean> added) {
            this.changedChildren = changedChildren;
            this.added = added;

        }

        private List<IServicePolicy> changedChildren;
        private List<Boolean> added;

        public List<IServicePolicy> getChangedChildren() {
            return changedChildren;
        }

        public void setChangedChildren(List<IServicePolicy> changedChildren) {
            this.changedChildren = changedChildren;
        }

        public List<Boolean> getAdded() {
            return added;
        }

        public void setAdded(List<Boolean> added) {
            this.added = added;
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.wst.ws.service.policy.listeners.IPolicyChildChangeListener#childChange(List<org.eclipse.wst.ws.service.policy.IServicePolicy>, List<java.lang.Boolean>)
     */
    public void childChange(List<IServicePolicy> child, List<Boolean> added) {
        //ignore events unless we are in a complex operation
        if (!bComplexOpCompleted)
            //cache the event until complex operation completed
            listChildChangeEvents.add(new ChildChangeEvent(child, added));

    }

    /**
     * @param child
     * @param added
     */
    private void processChildChangeEvent(ChildChangeEvent cce,
            Hashtable<String, IServicePolicy> htTopLevelAddtions) {
        List<IServicePolicy> changedServicePolicyList = cce.getChangedChildren();
        List<Boolean> changedServicePolicyAddedList = cce.getAdded();
        for (int i = 0; i < changedServicePolicyList.size(); i++) {
            IServicePolicy nextChangedServicePolicy = changedServicePolicyList.get(i);
            boolean added = changedServicePolicyAddedList.get(i);
            if (!added) {
                //delete node
                processChildChangeEventRemoveServicePolicy(nextChangedServicePolicy);
            } else {
                //an addition, only process top level additions as this is a recursive operation (adds children too)
                if (htTopLevelAddtions.containsKey(nextChangedServicePolicy.getId())) {
                    processChildChangeEventAddServicePolicy(nextChangedServicePolicy);
                }
            }
        }
    }

    /**
     * @param nextChangedServicePolicy
     */
    private void processChildChangeEventRemoveServicePolicy(IServicePolicy nextChangedServicePolicy) {
        boolean is4thLevelOrHigher = is4thLevelOrHigherPolicy(nextChangedServicePolicy);

        removeStateListener(nextChangedServicePolicy);

        if (is4thLevelOrHigher) {
            if (detailsPolicyTree.isVisible()) {
                TreeItem[] children = detailsPolicyTree.getItems();
                TreeItem found = null;
                for (int i = 0; i < children.length; i++) {
                    found = findPolicy(nextChangedServicePolicy, children[i]);
                    if (found != null)
                        break;
                }
                if (found != null && !found.isDisposed()) {
                    processChildChangeEventDeleteTreeItem(found);
                }
            }
        } else {
            TreeItem[] children = masterPolicyTree.getItems();
            TreeItem found = null;
            for (int i = 0; i < children.length; i++) {
                found = findPolicy(nextChangedServicePolicy, children[i]);
                if (found != null)
                    break;
            }
            if (found != null && !found.isDisposed()) {
                processChildChangeEventDeleteTreeItem(found);

            }

        }

    }

    /**
     * Delete the item, and choose set a new selection as appropriate from one of the policy trees 
     * @param deleteItem
     */
    private void processChildChangeEventDeleteTreeItem(TreeItem deleteItem) {
        Tree parentTree = deleteItem.getParent();
        Tree selectionTree = parentTree;
        TreeItem parentTreeItem = deleteItem.getParentItem();
        //index of found in the tree
        int idx = (parentTreeItem == null) ? parentTree.indexOf(deleteItem) : parentTreeItem.indexOf(deleteItem);
        //is index the last index in the tree
        boolean isLastIndex = (parentTreeItem == null)
                ? parentTree.indexOf(deleteItem) == parentTree.getItemCount() - 1
                : parentTreeItem.indexOf(deleteItem) == parentTreeItem.getItemCount() - 1;
        //number of siblings
        int siblingsCount = (parentTreeItem == null) ? parentTree.getItemCount() - 1
                : parentTreeItem.getItemCount() - 1;
        boolean hasParent = (parentTreeItem == null) ? parentTree.getItemCount() == 0
                : parentTreeItem.getItemCount() == 0;
        deleteItem.dispose();
        parentTree.redraw();
        TreeItem selectItem = null;
        if (siblingsCount == 0) {
            if (hasParent) {
                //no siblings, select parent
                if (parentTreeItem == null)
                    selectItem = parentTree.getItem(0);
                else
                    selectItem = parentTreeItem;
            } else {
                if (parentTree == detailsPolicyTree)
                    //select something from master tree
                    if (masterPolicyTree.getItems().length > 0) {
                        selectItem = masterPolicyTree.getItem(0);
                        selectionTree = masterPolicyTree;
                    }

            }

        } else {
            if (parentTreeItem == null) {
                if (isLastIndex)
                    selectItem = parentTree.getItem(idx - 1);
                else
                    selectItem = parentTree.getItem(idx);
            } else {
                if (isLastIndex)
                    selectItem = parentTreeItem.getItem(idx - 1);
                else
                    selectItem = parentTreeItem.getItem(idx);

            }
        }
        if (selectItem != null) {
            selectionTree.setSelection(selectItem);
            selectionTree.notifyListeners(SWT.Selection, new Event());
        }
    }

    /**
     * @param policyToAdd
     */
    private void processChildChangeEventAddServicePolicy(IServicePolicy policyToAdd) {
        boolean is4thLevelOrHigher = is4thLevelOrHigherPolicy(policyToAdd);
        IServicePolicy parent = policyToAdd.getParentPolicy();
        if (is4thLevelOrHigher) {
            if (detailsPolicyTree.isVisible()) {
                TreeItem[] children = detailsPolicyTree.getItems();
                TreeItem found = null;
                for (int i = 0; i < children.length; i++) {
                    found = findPolicy(parent, children[i]);
                    if (found != null)
                        break;
                }
                if (found != null && !found.isDisposed()) {
                    addPolicy(policyToAdd, found, false);
                }
            }
        } else {
            //less than 4th level policy
            TreeItem[] children = masterPolicyTree.getItems();
            TreeItem found = null;
            for (int i = 0; i < children.length; i++) {
                found = findPolicy(parent, children[i]);
                if (found != null)
                    break;
            }
            if (found != null && !found.isDisposed()) {
                addPolicy(policyToAdd, found, false);

            }
        }
    }

    /**
     * Find the policy within the tree ti
     * @param policyToFind
     */
    private TreeItem findPolicy(IServicePolicy policyToFind, TreeItem ti) {
        TreeItem toReturn = null;
        if (ti == null || !(ti.getData() instanceof IServicePolicy))
            return toReturn;
        IServicePolicy sp = (IServicePolicy) ti.getData();
        if (sp.getId().equals(policyToFind.getId()))
            return ti;
        else {
            TreeItem[] tiChildren = ti.getItems();
            for (int i = 0; i < tiChildren.length; i++) {
                toReturn = findPolicy(policyToFind, tiChildren[i]);
                if (toReturn != null)
                    break;
            }
        }
        return toReturn;
    }

    /**
     * Initializes states of the controls using default values in the preference
     * store.
     */
    private void initializeDefaults() {
        if (project == null)
            platform.restoreDefaults();
        else
            platform.restoreDefaults(project);

    }

    /* (non-Javadoc)
     * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
     */
    public void widgetSelected(SelectionEvent e) {
        if (e.getSource() == masterPolicyTree || e.getSource() == detailsPolicyTree) {
            TreeItem[] selectedItems;
            if (e.getSource() == masterPolicyTree)
                selectedItems = masterPolicyTree.getSelection();
            else
                selectedItems = detailsPolicyTree.getSelection();
            //possible that an event is fired and no nodes selected
            if (!(selectedItems.length > 0))
                return;
            List<IServicePolicy> sp = new Vector<IServicePolicy>();
            for (int i = 0; i < selectedItems.length; i++) {
                sp.add((IServicePolicy) selectedItems[i].getData());
            }
            //update the context sensitive help
            updateCSH(sp);
            updateInfoPanels(sp);

            if (e.getSource() == masterPolicyTree) {
                // if selected node in master tree is 3rd level & has
                // children, populate details tree
                if (is3rdLevelPolInMasterTreeWithChildren(sp)) {
                    populateDetailsPolicyTree(sp);
                } else {
                    //if expandable composite was visible, collapse and set invisible
                    if (excomposite.getVisible()) {
                        excomposite.setVisible(false);
                        excomposite.setExpanded(false);
                        expandedStateChanged(excomposite);
                    }
                }
            }
            addActionButtons(sp);
            lastSelectedSp = sp;

        } else {
            // an action control fired a change event
            Control actionControl = (Control) e.getSource();
            Object oldData = actionControl.getData();

            updatePolicy(actionControl);

            if (lastSelectedSp != null) {
                addActionButtons(lastSelectedSp);
            }

            //policy updates might delete nodes in the tree, so make sure action control not disposed
            IServicePolicy changedSP = null;
            if (!actionControl.isDisposed())
                changedSP = ((ActionControlData) actionControl.getData()).getSpList().get(0);
            error = validateAllPolicies(changedSP);
            //inform listeners that a control has changed, as composite may be in error now
            listener.widgetSelected(e);

            setFocusForLastControl(oldData);
        }
    }

    // The UI is continually recreating the action buttons, so the focus is
    // is lost when an action button is selected.  This method attempts
    // to find the newly created control that is associated with operation
    // that was previously selected.  The focus is then reestablished for this 
    // control.
    private void setFocusForLastControl(Object oldData) {
        if (oldData != null && oldData instanceof ActionControlData) {
            ActionControlData controlData = (ActionControlData) oldData;
            IPolicyOperation operation = controlData.getPoList().get(0);
            Control[] compositeControls = operationsComposite.getChildren();

            for (Control newControl : compositeControls) {
                Object newData = newControl.getData();

                if (newData != null && newData instanceof ActionControlData) {
                    IPolicyOperation newOperation = ((ActionControlData) newData).poList.get(0);

                    if (newOperation == operation) {
                        newControl.setFocus();
                        break;
                    }
                }
            }
        }
    }

    /**
     * Populate the details policy tree with sp and their children
     * @param sp a list of top level details tree service policies
     */
    private void populateDetailsPolicyTree(List<IServicePolicy> sp) {
        List<IServicePolicy> childpolicyList = sp.get(0).getChildren();
        detailsPolicyTree.removeAll();
        for (IServicePolicy policy : childpolicyList) {
            addPolicy(policy, detailsPolicyTree, false);
        }
        // update details preference tree
        TreeItem[] treeItems = detailsPolicyTree.getItems();
        for (int i = 0; i < treeItems.length; i++) {
            updateValidStates(treeItems[i], true);
        }

        excomposite.setVisible(true);
        excomposite
                .setText(sp.get(0).getDescriptor().getLongName() + " (" + WstSPUIPluginMessages.TEXT_DETAILS + ")"); //$NON-NLS-1$ //$NON-NLS-2$
        // text has changed, so need to force layout so updated
        excomposite.layout();
    }

    /**
     * Updates the details and dependencies panels for the first service
     * policy in spList
     * @param spList a list of selected service policies
     */
    private void updateInfoPanels(List<IServicePolicy> spList) {
        String desc = (spList.get(0) == null || spList.get(0).getDescriptor() == null
                || spList.get(0).getDescriptor().getDescription() == null) ? "" //$NON-NLS-1$
                        : spList.get(0).getDescriptor().getDescription();
        text_DetailsPanel_description.setText(desc);
        text_DetailsPanel_dependencies.setText(getDependanciesText(spList.get(0)));
    }

    /**
     * Updates the context sensitive help for the composite with 
     * that defined for the first policy in spList (or a parent of 
     * first policy in spList policy does not define any CSH)
     * @param spList the list of selected service policies
     */
    private void updateCSH(List<IServicePolicy> spList) {
        String CSH_ID = (spList.get(0) == null || spList.get(0).getDescriptor() == null) ? null
                : spList.get(0).getDescriptor().getContextHelpId();
        IServicePolicy parentSP = spList.get(0);
        while ((CSH_ID == null || CSH_ID.length() == 0)) {
            parentSP = parentSP.getParentPolicy();
            if (parentSP == null)
                break;
            CSH_ID = (parentSP.getDescriptor() == null) ? null : parentSP.getDescriptor().getContextHelpId();
        }
        if (CSH_ID == null)
            CSH_ID = INFOPOP_SPPP_PAGE;
        helpSystem.setHelp(this, CSH_ID);
    }

    /**
     * Validates all Service Policies in the model
     * 
     * @param IServicePolicy
     *            the focus policy, i.e. one that changed state
     * @return IStatus null if no Service policies in error, otherwise a Service
     *         Policy error (the focucPolicy error if it is in error).
     *         Postcondition- the error property is updated.
     */
    private IStatus validateAllPolicies(IServicePolicy focusPolicy) {
        // remove all errors as going to re-validate
        allErrors.clear();
        // re-validate all policies in model
        List<IServicePolicy> servicePolicyList = platform.getRootServicePolicies(null);
        for (IServicePolicy servicePolicyItem : servicePolicyList) {
            validatePolicy(servicePolicyItem);
        }
        // get the error for the selected/focusPolicy
        IStatus error = (focusPolicy == null) ? null : allErrors.get(focusPolicy.getId());
        //if there was no error for the selected/focusPolicy, get the first error in the list of errors
        if (error == null)
            if (!allErrors.isEmpty())
                error = allErrors.get(allErrors.keys().nextElement());

        // update master policy tree nodes denoting node error status
        TreeItem[] treeItems = masterPolicyTree.getItems();
        for (int i = 0; i < treeItems.length; i++) {
            updateValidStates(treeItems[i], false);
        }
        if (detailsPolicyTree.isVisible()) {
            // update details preference tree nodes denoting node error status
            treeItems = detailsPolicyTree.getItems();
            for (int i = 0; i < treeItems.length; i++) {
                updateValidStates(treeItems[i], false);
            }

        }
        return error;
    }

    /**
     * Updates the policy trees to reflect valid or invalid states, invalid states are 
     * propagated up through the tree, and up to master tree if updateDetailsOnly is false
     * @param ti the TreeItem to update, updates ti and all it's children
     * @param updateDetailsOnly if false then propagate invalid states into
     * master tree 
     */
    private void updateValidStates(TreeItem ti, boolean updateDetailsOnly) {
        // assume tree item is valid for now
        IServicePolicy sp = (IServicePolicy) ti.getData();
        setValidPolicyState(ti);

        if (allErrors.containsKey(sp.getId()))
            setInvalidPolicyState(ti, updateDetailsOnly);
        TreeItem[] treeItems = ti.getItems();
        if (ti.getItems().length == 0) {
            if (sp.getChildren().size() != 0 && policyChildrenInvalid(sp))
                setInvalidPolicyState(ti, updateDetailsOnly);
        } else {
            for (int i = 0; i < treeItems.length; i++) {

                updateValidStates(treeItems[i], updateDetailsOnly);
            }
        }

    }

    /**
     * Checks whether the sp has invalid children
     * @param sp the service policy whose children may be invalid
     * @return true if sp has an invalid child
     */
    private boolean policyChildrenInvalid(IServicePolicy sp) {
        boolean toReturn = false;
        List<IServicePolicy> servicePolicyList = sp.getChildren();
        for (IServicePolicy servicePolicyItem : servicePolicyList) {
            if (allErrors.containsKey(servicePolicyItem.getId()))
                return true;
            else
                toReturn = policyChildrenInvalid(servicePolicyItem);
        }
        return toReturn;
    }

    /**
     * Validates the Service Policies for the sp and all it's children, creating 
     * error status objects for any unsatisfied relationships
     * 
     * @param sp
     *            the service policy to validate
     */
    private void validatePolicy(IServicePolicy sp) {
        String operationLongName;
        String operationSelectionLongName;
        String dependantPolicyShortName;
        List<IPolicyRelationship> relationShipList = sp.getRelationships();
        String currentValueID = null;
        if (relationShipList != null && relationShipList.size() > 0) {
            for (IPolicyRelationship relationShipItem : relationShipList) {
                //operations for the policy
                List<IPolicyOperation> policyOperationsList = platformUI
                        .getOperations(relationShipItem.getPolicyEnumerationList().getPolicy(), project == null);
                //default operation name
                operationLongName = ""; //$NON-NLS-1$
                for (IPolicyOperation policyOperationItem : policyOperationsList) {
                    if (policyOperationItem.isUseDefaultData()) {
                        operationLongName = policyOperationItem.getDescriptor().getLongName();
                        currentValueID = policyOperationItem.getStateItem(project);
                        break;
                    }
                }
                // a list of states
                List<IStateEnumerationItem> spStateEnumerationList = relationShipItem.getPolicyEnumerationList()
                        .getEnumerationList();
                // a list of related policies and their acceptable states to satisfy a stateEnumerationItem in spStateEnumerationList
                List<IPolicyEnumerationList> relatedPolicies = relationShipItem.getRelatedPolicies();
                for (IStateEnumerationItem stateEnumerationItem : spStateEnumerationList) {
                    if (stateEnumerationItem.getId() != currentValueID)
                        continue;
                    //long name of the related policy operation
                    operationSelectionLongName = stateEnumerationItem.getLongName();
                    for (IPolicyEnumerationList relatedPolicyEnumerationItem : relatedPolicies) {
                        dependantPolicyShortName = relatedPolicyEnumerationItem.getPolicy().getDescriptor()
                                .getShortName();
                        //the list of related sp states that satisfy the stateEnumerationItem
                        List<IStateEnumerationItem> relatedSPStateEnumerationList = relatedPolicyEnumerationItem
                                .getEnumerationList();
                        //the list of valid ids for the related service policy operation
                        List<String> validIds = new Vector<String>();
                        for (int i = 0; i < relatedSPStateEnumerationList.size(); i++) {
                            validIds.add(relatedSPStateEnumerationList.get(i).getId());

                        }
                        //get the current value of the related service policy default operation
                        String currentItemID = ""; //$NON-NLS-1$
                        List<IPolicyOperation> relatedServicePolicyPolicyOperationsList = platformUI
                                .getOperations(relatedPolicyEnumerationItem.getPolicy(), project == null);
                        IPolicyOperation relatedPolicyOperationItem = null;
                        for (IPolicyOperation relatedServicePolicyPolicyOperationItem : relatedServicePolicyPolicyOperationsList) {
                            if (relatedServicePolicyPolicyOperationItem.isUseDefaultData()) {
                                currentItemID = relatedServicePolicyPolicyOperationItem.getStateItem(project);
                                relatedPolicyOperationItem = relatedServicePolicyPolicyOperationItem;
                                break;
                            }
                        }

                        if (!validIds.contains(currentItemID)) {
                            // policy state is invalid
                            IStatus error = createUnsatisfiedRelationshipError(sp, operationLongName,
                                    operationSelectionLongName, dependantPolicyShortName,
                                    relatedPolicyOperationItem, relatedSPStateEnumerationList);
                            allErrors.put(sp.getId(), error);

                        }

                    }

                }

            }
        }

        List<IServicePolicy> servicePolicyChildrenList = sp.getChildren();
        for (IServicePolicy servicePolicyChildrenItem : servicePolicyChildrenList) {
            validatePolicy(servicePolicyChildrenItem);

        }

    }

    /**
     * Sets the icon for the ti denoting it as valid
     * @param ti a valid tree item
     */
    private void setValidPolicyState(TreeItem ti) {
        IServicePolicy sp = (IServicePolicy) ti.getData();
        setImage(ti, sp, getIconOverlayInfo(sp, false));
    }

    /**
     * Sets the icon for ti denoting it as invalid, as well as ti's parent tree items
     * @param ti an invalid tree item
     * @param updateDetailsOnly true indicates do not update parent tree items only in master tree
     */
    private void setInvalidPolicyState(TreeItem ti, boolean updateDetailsOnly) {
        IServicePolicy SP = (IServicePolicy) ti.getData();
        setImage(ti, SP, getIconOverlayInfo(SP, true));
        TreeItem parent;
        if (ti.getParent() == masterPolicyTree || updateDetailsOnly)
            parent = ti.getParentItem();
        else
            parent = (ti.getParentItem() == null && SP.getParentPolicy() != null)
                    ? getParentInMasterTree(SP.getId())
                    : ti.getParentItem();
        while (parent != null) {
            setImage(parent, (IServicePolicy) parent.getData(),
                    getIconOverlayInfo((IServicePolicy) parent.getData(), true));
            if (updateDetailsOnly)
                parent = parent.getParentItem();
            else {
                parent = (parent.getParentItem() == null
                        && ((IServicePolicy) parent.getData()).getParentPolicy() != null)
                                ? getParentInMasterTree(
                                        ((IServicePolicy) parent.getData()).getParentPolicy().getId())
                                : parent.getParentItem();
            }

        }
    }

    /**
     * Creates an error status object for the sp. The error message contains information about 
     * an unsatisfied relationship
     * @param sp
     * @param operationLongName
     * @param operationSelectionLongName
     * @param dependantPolicyShortName
     * @param relatedOperation
     * @param relatedOperationAcceptableValues
     * @return an Error status
     */
    private IStatus createUnsatisfiedRelationshipError(IServicePolicy sp, String operationLongName,
            String operationSelectionLongName, String dependantPolicyShortName, IPolicyOperation relatedOperation,
            List<IStateEnumerationItem> relatedOperationAcceptableValues) {
        String dependantOperationShortName;
        String dependantOperationSelectionShortNameList;
        dependantOperationShortName = relatedOperation.getDescriptor().getShortName();
        dependantOperationSelectionShortNameList = new String();
        for (int i = 0; i < relatedOperationAcceptableValues.size(); i++) {
            IStateEnumerationItem item = relatedOperationAcceptableValues.get(i);
            if (i != 0)
                dependantOperationSelectionShortNameList += " | "; //$NON-NLS-1$
            dependantOperationSelectionShortNameList += item.getShortName();

        }
        String[] args = { sp.getDescriptor().getLongName(), operationLongName, operationSelectionLongName,
                dependantPolicyShortName, dependantOperationShortName, dependantOperationSelectionShortNameList };
        IStatus error = new Status(Status.ERROR, ServicePolicyActivatorUI.PLUGIN_ID,
                NLS.bind(WstSPUIPluginMessages.SERVICEPOLICIES_DEPENDENCY_ERROR, args));
        return error;
    }

    /**
     * @param parentID the id to find in the master tree
     * @return a TreeItem with the id in the master tree
     */
    private TreeItem getParentInMasterTree(String parentID) {
        TreeItem parent = null;
        TreeItem[] rootNodes = masterPolicyTree.getItems();
        for (int i = 0; i < rootNodes.length; i++) {
            parent = findChildNode(rootNodes[i], parentID);
            if (parent != null)
                break;
        }
        return parent;

    }

    /**
     * Returns a TreeItem associated with a child policy of ti
     * @param ti
     * @param id the policy id of a child node of ti
     * @return null if there is no child policy matching id
     */
    private TreeItem findChildNode(TreeItem ti, String id) {
        TreeItem toReturn = null;
        if (((IServicePolicy) ti.getData()).getId().equals(id)) {
            toReturn = ti;
        } else {
            TreeItem[] childItems = ti.getItems();
            for (int i = 0; i < childItems.length; i++) {
                toReturn = findChildNode(childItems[i], id);
            }

        }
        return toReturn;

    }

    /**
     * Returns dependancy information about sp, that is information about
     * it's relationships (if any) with other service policies
     * @param sp the service policy to get dependency info about
     * @return
     */
    private String getDependanciesText(IServicePolicy sp) {
        // the relationships defined for the policy
        List<IPolicyRelationship> relationShipList = sp.getRelationships();
        if (relationShipList == null || relationShipList.size() == 0)
            return WstSPUIPluginMessages.SERVICEPOLICIES_DEPENDENCIES_NONE;
        String toReturn = new String();

        String operationLongName;
        String operationSelectionLongName;
        String dependantPolicyShortName;
        String dependantOperationShortName;
        String dependantOperationSelectionShortNameList;

        for (IPolicyRelationship relationShipItem : relationShipList) {
            List<IPolicyOperation> policyOperationsList = platformUI
                    .getOperations(relationShipItem.getPolicyEnumerationList().getPolicy(), project == null);
            operationLongName = ""; //$NON-NLS-1$
            for (IPolicyOperation policyOperationItem : policyOperationsList) {
                if (policyOperationItem.isUseDefaultData()) {
                    operationLongName = policyOperationItem.getDescriptor().getLongName();
                    break;
                }
            }
            // policies associated with the relationship item
            List<IPolicyEnumerationList> relatedPolicies = relationShipItem.getRelatedPolicies();
            List<IStateEnumerationItem> spStateEnumerationList = relationShipItem.getPolicyEnumerationList()
                    .getEnumerationList();
            for (IStateEnumerationItem stateEnumerationItem : spStateEnumerationList) {
                operationSelectionLongName = stateEnumerationItem.getLongName();
                for (IPolicyEnumerationList relatedPolicyEnumerationItem : relatedPolicies) {
                    dependantPolicyShortName = relatedPolicyEnumerationItem.getPolicy().getDescriptor()
                            .getShortName();
                    List<IStateEnumerationItem> relatedSPStateEnumerationList = relatedPolicyEnumerationItem
                            .getEnumerationList();
                    List<IPolicyOperation> relatedPolicyOperationsList = platformUI
                            .getOperations(relatedPolicyEnumerationItem.getPolicy(), (project == null));
                    dependantOperationShortName = ""; //$NON-NLS-1$
                    for (IPolicyOperation relatedPolicyOperationItem : relatedPolicyOperationsList) {
                        if (relatedPolicyOperationItem.isUseDefaultData())
                            dependantOperationShortName = relatedPolicyOperationItem.getDescriptor().getShortName();
                        break;
                    }

                    dependantOperationSelectionShortNameList = new String();
                    for (int i = 0; i < relatedSPStateEnumerationList.size(); i++) {
                        IStateEnumerationItem item = relatedSPStateEnumerationList.get(i);
                        if (i != 0)
                            dependantOperationSelectionShortNameList += " | "; //$NON-NLS-1$
                        dependantOperationSelectionShortNameList += item.getShortName();

                    }
                    String[] args = { operationLongName, operationSelectionLongName, dependantPolicyShortName,
                            dependantOperationShortName, dependantOperationSelectionShortNameList };
                    toReturn += NLS.bind(WstSPUIPluginMessages.SERVICEPOLICIES_DEPENDENCIES, args) + "\r\n"; //$NON-NLS-1$

                }

            }

        }
        return toReturn;
    }

    /**
     * Saves to the preference store the widget data (widget data contains all 
     * service policy operations the control applies to)
     * @param actionControl a widget who's data needs to be saved to the preference store
     * 
     */
    private void updatePolicy(Control actionControl) {

        List<IPolicyOperation> policyOperationList = ((ActionControlData) actionControl.getData()).getPoList();
        IPolicyOperation po0 = policyOperationList.get(0);

        List<IServicePolicy> servicePolicyList = ((ActionControlData) actionControl.getData()).getSpList();
        if (actionControl instanceof Button && po0.getOperationKind().equals(OperationKind.complex))
            updateComplexOperationPreference(po0, servicePolicyList);
        else
            for (IPolicyOperation policyOperationItem : policyOperationList) {

                updateSelectionOperationPreference(actionControl, policyOperationItem);
            }

    }

    public boolean okToLeave() {
        return allErrors.size() == 0;

    }

    /**
     * Saves value of enumeration, selection, and iconselection type operations
     * to the preference store
     * @param actionControl the action control containing the value to set for the preference
     * @param po 
     * @param sp
     */
    private void updateSelectionOperationPreference(Control actionControl, IPolicyOperation po) {
        String selectedValue;

        // We are updating the state of a service policy.  We don't want this
        // update to trigger our state change listener.
        enableStateListener(false);

        if (actionControl instanceof Combo) {
            selectedValue = ((Combo) actionControl).getText();
            List<IStateEnumerationItem> enumItemList = ServicePolicyPlatform.getInstance()
                    .getStateEnumeration(po.getEnumerationId());
            for (IStateEnumerationItem enumItem : enumItemList) {
                if (enumItem.getLongName().equals(selectedValue)) {
                    po.setStateItem(project, enumItem.getId());
                    break;
                }
            }

        } else {
            if (((Button) actionControl).getSelection()) {
                po.setStateItem(project, "org.eclipse.wst.true"); //$NON-NLS-1$
            } else
                po.setStateItem(project, "org.eclipse.wst.false"); //$NON-NLS-1$

        }

        // Reenable our state change listener.
        enableStateListener(true);
    }

    private void addStateListener(IServicePolicy policy) {
        if (stateChangePolicySet.contains(policy))
            return;

        if (project == null) {
            policy.getPolicyState().addPolicyStateChangeListener(stateChangeListener, false);
        } else {
            policy.getPolicyState(project).addPolicyStateChangeListener(stateChangeListener, false);
        }

        stateChangePolicySet.add(policy);
    }

    private void removeStateListener(IServicePolicy policy) {
        if (stateChangePolicySet.contains(policy))
            return;

        if (project == null) {
            policy.getPolicyState().removePolicyStateChangeListener(stateChangeListener);
        } else {
            policy.getPolicyState(project).removePolicyStateChangeListener(stateChangeListener);
        }

        stateChangePolicySet.remove(policy);
    }

    private void enableStateListener(boolean enabled) {
        stateChangeEnabled = enabled;
    }

    /**
     * Launches the complex operation for the list of service policies
     * @param po a "complex" policy operation 
     * @param sps a list of service policies to apply the operation to
     */
    private void updateComplexOperationPreference(IPolicyOperation po, List<IServicePolicy> sps) {
        //complex operations can result in added and deleted nodes, cache them until operation completed
        bComplexOpCompleted = false;
        listChildChangeEvents = new Vector<ChildChangeEvent>();
        try {
            po.launchOperation(sps);
        } catch (RuntimeException e) {
            //just want to make sure finally clause is executed so can update UI of additions/deletions
        } finally {
            bComplexOpCompleted = true;
            Hashtable<String, IServicePolicy> topLevelAdds = getHighestLevelChildren(listChildChangeEvents, true);
            for (ChildChangeEvent childChangeEventItem : listChildChangeEvents) {
                processChildChangeEvent(childChangeEventItem, topLevelAdds);
            }

        }
    }

    private Hashtable<String, IServicePolicy> getHighestLevelChildren(List<ChildChangeEvent> htChildChangeEvents,
            boolean forAddOnly) {
        Hashtable<String, IServicePolicy> toReturn = new Hashtable<String, IServicePolicy>();
        Iterator<ChildChangeEvent> elements = htChildChangeEvents.iterator();
        ChildChangeEvent next;
        Hashtable<String, IServicePolicy> allSps = new Hashtable<String, IServicePolicy>();
        while (elements.hasNext()) {
            next = elements.next();
            List<IServicePolicy> changedChildrenList = next.getChangedChildren();
            List<Boolean> addedList = next.getAdded();
            for (int i = 0; i < changedChildrenList.size(); i++) {
                if (forAddOnly && addedList.get(i).booleanValue() == true)
                    allSps.put(changedChildrenList.get(i).getId(), changedChildrenList.get(i));
            }
        }
        Enumeration<IServicePolicy> servicePolicyElements = allSps.elements();
        IServicePolicy nextServicePolicyElement;
        while (servicePolicyElements.hasMoreElements()) {
            nextServicePolicyElement = servicePolicyElements.nextElement();
            if (nextServicePolicyElement.getParentPolicy() == null
                    || !allSps.containsKey(nextServicePolicyElement.getParentPolicy().getId())) {
                toReturn.put(nextServicePolicyElement.getId(), nextServicePolicyElement);
            }
        }
        return toReturn;
    }

    public void dispose() {
        super.dispose();
        iconManager.dispose();

        for (IServicePolicy policy : stateChangePolicySet) {
            IPolicyState state = project == null ? policy.getPolicyState() : policy.getPolicyState(project);

            state.removePolicyStateChangeListener(stateChangeListener);
        }
    }

    /**
     * Create UI widgets for operations applicable to the list of service policies sp
     * @param spList the list of service policies to create UI widgets, the UI widgets
     * will be used to define operations/preferences to apply to the service policies in the list
     */
    private void addActionButtons(List<IServicePolicy> spList) {

        // remove existing action controls
        Control[] toRemove = operationsComposite.getChildren();
        for (int i = 0; i < toRemove.length; i++) {
            toRemove[i].dispose();
        }
        List<List<IPolicyOperation>> policyOperationListList = platformUI.getOperationsList(spList,
                (project == null));

        for (List<IPolicyOperation> policyOperationList : policyOperationListList) {
            if (policyOperationList.size() > 0) {
                if (policyOperationList.get(0).getOperationKind() == IPolicyOperation.OperationKind.complex) {
                    addComplexOperationUI(policyOperationList, spList);
                } else
                    addSelectionOperationUI(policyOperationList, spList);
            }

        }
        // just removed and added some controls so force composite
        // to re-layout it's controls
        operationsScrolledComposite.setMinSize(operationsComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
        operationsComposite.layout();
    }

    /**
     * Creates UI widgets for policy operations of enumeration, selection, and 
     * iconselection types
     * @param po the policy operation to create a UI widget for
     * @param sp the service policies this operation will apply to
     */
    private void addSelectionOperationUI(List<IPolicyOperation> pos, List<IServicePolicy> sps) {
        //use the first operation instance in the list for data
        IPolicyOperation po0 = pos.get(0);
        //IServicePolicy sp0 = pos.get(0).getServicePolicy();
        IDescriptor d = po0.getDescriptor();
        Control selectionControl;
        if (po0.getOperationKind() == IPolicyOperation.OperationKind.enumeration) {
            Label l = new Label(operationsComposite, SWT.NONE);
            l.setText(d.getLongName() + ":"); //$NON-NLS-1$
            GridData comboGD = new GridData(SWT.FILL, SWT.FILL, true, false);

            Combo cb = new Combo(operationsComposite, SWT.DROP_DOWN | SWT.READ_ONLY);
            cb.setLayoutData(comboGD);
            selectionControl = cb;
            cb.addSelectionListener(this);
            List<IStateEnumerationItem> enumItemList = ServicePolicyPlatform.getInstance()
                    .getStateEnumeration(po0.getEnumerationId());
            for (IStateEnumerationItem enumItem : enumItemList) {
                cb.add(enumItem.getLongName());
            }
            l.setEnabled(po0.isEnabled(sps));
            cb.setEnabled(po0.isEnabled(sps));
            if (cb.isEnabled())
                cb.setText(getEnumerationOperationCurrentSelection(po0));
        } else {
            // a selection or icon
            Button checkBox = new Button(operationsComposite, SWT.CHECK);
            selectionControl = checkBox;
            checkBox.addSelectionListener(this);
            GridData checkBoxGD = new GridData();
            checkBoxGD.horizontalSpan = 2;
            checkBox.setLayoutData(checkBoxGD);
            checkBox.setText(d.getLongName());
            checkBox.setEnabled(po0.isEnabled(sps));
            if (checkBox.isEnabled())
                checkBox.setSelection(getSelectionOperationCurrentSelection(po0));

        }

        selectionControl.setData(new ActionControlData(sps, pos));

    }

    /**
     * @param sp a service policy with a selection or iconselection operation defined
     * @return true if the operation is currently selected
     */
    private boolean getSelectionOperationCurrentSelection(IPolicyOperation po) {
        return po.getStateItem(project).equals("org.eclipse.wst.true") ? true : false; //$NON-NLS-1$

    }

    /**
     * @param sp a service policy with an enumeration operation defined
     * @return the currently selected enumeration item
     */
    private String getEnumerationOperationCurrentSelection(IPolicyOperation po) {
        String currentSelection = ""; //$NON-NLS-1$
        List<IStateEnumerationItem> enumItemList = ServicePolicyPlatform.getInstance()
                .getStateEnumeration(po.getEnumerationId());
        for (IStateEnumerationItem enumItem : enumItemList) {
            if (enumItem.getId().equals(po.getStateItem(project))) {
                currentSelection = enumItem.getLongName();
                break;
            }
        }
        return currentSelection;
    }

    /**
     * Creates UI widgets for policy operations of complex type
     * @param po the policy operation to create a UI widget for
     * @param sps the service policies this operation will apply to
     */
    private void addComplexOperationUI(List<IPolicyOperation> pos, List<IServicePolicy> sps) {
        IPolicyOperation po0 = pos.get(0);
        IDescriptor d = po0.getDescriptor();
        Button pushButton = new Button(operationsComposite, SWT.PUSH);
        GridData pushButtonGD = new GridData();
        pushButtonGD.horizontalSpan = 2;
        pushButton.setLayoutData(pushButtonGD);
        pushButton.setText(d.getLongName());
        pushButton.addSelectionListener(this);
        pushButton.setData(new ActionControlData(sps, pos));
        pushButton.setEnabled(po0.isEnabled(sps));

    }

    /* (non-Javadoc)
     * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
     */
    public void widgetDefaultSelected(SelectionEvent e) {

    }

    /**
     * @return the error that should be shown, if there are multiple validation errors,
     * return the validation error for a service policy that just changes state (often the
     * selected service policy)
     */
    public IStatus getError() {
        return error;
    }

    private class StateChangeListener implements IPolicyStateChangeListener {
        public void policyStateChange(IServicePolicy policy, String key, String oldValue, String newValue) {
            // We are not enabled for state change events so we will ignore this event.
            if (!stateChangeEnabled)
                return;

            error = validateAllPolicies(policy);

            //inform listeners that a control has changed, as composite may be in error now
            listener.widgetSelected(null);
        }
    }
}