org.eclipse.bpel.ui.util.BPELUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.bpel.ui.util.BPELUtil.java

Source

/*******************************************************************************
 * Copyright (c) 2005 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
 *******************************************************************************/
package org.eclipse.bpel.ui.util;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.xml.namespace.QName;

import org.eclipse.bpel.common.extension.model.ExtensionMap;
import org.eclipse.bpel.common.ui.ImageUtils;
import org.eclipse.bpel.common.ui.details.viewers.ComboViewer;
import org.eclipse.bpel.common.ui.details.widgets.DecoratedLabel;
import org.eclipse.bpel.common.ui.markers.ModelMarkerUtil;
import org.eclipse.bpel.model.BPELFactory;
import org.eclipse.bpel.model.BPELPackage;
import org.eclipse.bpel.model.Catch;
import org.eclipse.bpel.model.CompensateScope;
import org.eclipse.bpel.model.CorrelationSet;
import org.eclipse.bpel.model.CorrelationSets;
import org.eclipse.bpel.model.Flow;
import org.eclipse.bpel.model.ForEach;
import org.eclipse.bpel.model.Invoke;
import org.eclipse.bpel.model.OnEvent;
import org.eclipse.bpel.model.PartnerLink;
import org.eclipse.bpel.model.PartnerLinks;
import org.eclipse.bpel.model.Process;
import org.eclipse.bpel.model.ReferenceVariable;
import org.eclipse.bpel.model.ReferenceVariables;
import org.eclipse.bpel.model.Scope;
import org.eclipse.bpel.model.Sequence;
import org.eclipse.bpel.model.Variable;
import org.eclipse.bpel.model.Variables;
import org.eclipse.bpel.model.adapters.AdapterRegistry;
import org.eclipse.bpel.model.messageproperties.MessagepropertiesPackage;
import org.eclipse.bpel.model.messageproperties.PropertyAlias;
import org.eclipse.bpel.model.partnerlinktype.PartnerlinktypePackage;
import org.eclipse.bpel.model.util.BPELUtils;
import org.eclipse.bpel.names.NCNameWordDetector;
import org.eclipse.bpel.ui.BPELEditor;
import org.eclipse.bpel.ui.BPELUIPlugin;
import org.eclipse.bpel.ui.IBPELUIConstants;
import org.eclipse.bpel.ui.Messages;
import org.eclipse.bpel.ui.Policy;
import org.eclipse.bpel.ui.adapters.BPELUIAdapterFactory;
import org.eclipse.bpel.ui.adapters.BPELUIExtensionAdapterFactory;
import org.eclipse.bpel.ui.adapters.BPELUIMessagePropertiesAdapterFactory;
import org.eclipse.bpel.ui.adapters.BPELUIPartnerLinkTypeAdapterFactory;
import org.eclipse.bpel.ui.adapters.BPELUIWSDLAdapterFactory;
import org.eclipse.bpel.ui.adapters.BPELUIWSILAdapterFactory;
import org.eclipse.bpel.ui.adapters.BPELUIXSDAdapterFactory;
import org.eclipse.bpel.ui.adapters.IContainer;
import org.eclipse.bpel.ui.adapters.ILabeledElement;
import org.eclipse.bpel.ui.adapters.INamedElement;
import org.eclipse.bpel.ui.bpelactions.AbstractBPELAction;
import org.eclipse.bpel.ui.dialogs.NamespaceMappingDialog;
import org.eclipse.bpel.ui.editparts.BPELEditPart;
import org.eclipse.bpel.ui.editparts.FlowEditPart;
import org.eclipse.bpel.ui.editparts.InvokeEditPart;
import org.eclipse.bpel.ui.editparts.LinkEditPart;
import org.eclipse.bpel.ui.editparts.ScopeEditPart;
import org.eclipse.bpel.ui.editparts.StartNodeEditPart;
import org.eclipse.bpel.ui.editparts.borders.GradientBorder;
import org.eclipse.bpel.ui.editparts.util.OverlayCompositeImageDescriptor;
import org.eclipse.bpel.ui.extensions.ActionDescriptor;
import org.eclipse.bpel.ui.extensions.BPELUIRegistry;
import org.eclipse.bpel.ui.uiextensionmodel.ActivityExtension;
import org.eclipse.bpel.ui.uiextensionmodel.UiextensionmodelPackage;
import org.eclipse.bpel.wsil.model.inspection.InspectionPackage;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.gef.AccessibleEditPart;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.Tool;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import org.eclipse.wst.wsdl.Definition;
import org.eclipse.wst.wsdl.Fault;
import org.eclipse.wst.wsdl.Input;
import org.eclipse.wst.wsdl.Message;
import org.eclipse.wst.wsdl.Operation;
import org.eclipse.wst.wsdl.Output;
import org.eclipse.wst.wsdl.PortType;
import org.eclipse.wst.wsdl.WSDLPackage;
import org.eclipse.wst.wsdl.util.WSDLResourceImpl;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDPackage;

/**
 * BPELUtil is a place to put *static* helper methods for the BPEL editor.
 * 
 * Note that helpers which have specifically to do with accessing model objects are
 * usually found in the ModelHelper class.
 */
public class BPELUtil {

    private static final ReferenceVariable[] EMPTY_REFERENCE_VARIABLE_ARRAY = new ReferenceVariable[0];
    private static final Variable[] EMPTY_VARIABLE_ARRAY = new Variable[0];
    private static final PartnerLink[] EMPTY_PARTNERLINK_ARRAY = new PartnerLink[0];
    private static final CorrelationSet[] EMPTY_CORRELATIONSET_ARRAY = new CorrelationSet[0];

    /**
     * This global variable stores the path of the last WSDL file selected with
     * a WorkbenchFileSelectionDialog.
     */
    public static IPath lastWSDLFilePath = null;
    /**
     * Global variable storing the path of the last BPEL file selected
     */
    public static IPath lastBPELFilePath = null;

    /**
     * Global variable storing the path of the last XSD file selected
     */
    public static IPath lastXSDFilePath;

    static {
        AdapterRegistry.INSTANCE.registerAdapterFactory(BPELPackage.eINSTANCE, BPELUIAdapterFactory.getInstance());

        AdapterRegistry.INSTANCE.registerAdapterFactory(WSDLPackage.eINSTANCE,
                BPELUIWSDLAdapterFactory.getInstance());

        AdapterRegistry.INSTANCE.registerAdapterFactory(PartnerlinktypePackage.eINSTANCE,
                BPELUIPartnerLinkTypeAdapterFactory.getInstance());

        AdapterRegistry.INSTANCE.registerAdapterFactory(XSDPackage.eINSTANCE,
                BPELUIXSDAdapterFactory.getInstance());

        AdapterRegistry.INSTANCE.registerAdapterFactory(MessagepropertiesPackage.eINSTANCE,
                BPELUIMessagePropertiesAdapterFactory.getInstance());

        AdapterRegistry.INSTANCE.registerAdapterFactory(UiextensionmodelPackage.eINSTANCE,
                BPELUIExtensionAdapterFactory.getInstance());

        AdapterRegistry.INSTANCE.registerAdapterFactory(InspectionPackage.eINSTANCE,
                BPELUIWSILAdapterFactory.getInstance());

    }

    /**
     * Register adapter factory for the given EClass.
     * 
     * @param key
     * @param factory
     */

    public static void registerAdapterFactory(EClass key, AdapterFactory factory) {
        AdapterRegistry.INSTANCE.registerAdapterFactory(key, factory);
    }

    public static void registerAdapterFactory(EPackage key, AdapterFactory factory) {
        AdapterRegistry.INSTANCE.registerAdapterFactory(key, factory);
    }

    static Class<?> adapterInterface(Object type) {

        if (type instanceof Class) {
            return (Class) type;
        }

        if (type instanceof String) {
            try {
                return Class.forName((String) type);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        throw new RuntimeException("Adapter type " + type + " is not understood."); //$NON-NLS-1$ //$NON-NLS-2$
    }

    /**
     * @param <T>
     * @param target
     * @param clazz
     * @return the adapted interface or object
     */

    @SuppressWarnings("unchecked")

    public static <T extends Object> T adapt(Object target, Class<T> clazz) {
        return AdapterRegistry.INSTANCE.adapt(target, clazz);
    }

    /**
     * This method tries the registered adapter factories one by one, returning
     * the first non-null result it gets.  If none of the factories can adapt
     * the result, it returns null.
     * @param target target object 
     * @param type type of the adapter to find
     * @return the adapter for the target.
     */

    public static Object adapt(Object target, Object type) {
        return AdapterRegistry.INSTANCE.adapt(target, type);
    }

    /**
     * Create an adapter for the given target of the given type. 
     * In addition, pass a context object to the adapter(s) of the target. 
     * 
     * The idea is that some adapters can be stateful and depend not only 
     * on the objects that they wrap, but also on some other context that is needed
     * to completely and correctly implement the interface for which the adaptor is
     * needed.
     * 
     * Adapters that are stateless, should ignore any notifications sent to them.
     *  
     * @param target the target object
     * @param type the type it wants to adapt to
     * @param context the context object
     * 
     * @return the adapter
     */
    public static Object adapt(Object target, Object type, Object context) {
        return AdapterRegistry.INSTANCE.adapt(target, type, context);
    }

    /**
     * Returns the effective EClass for a custom activity (action).
     */
    public static EClass getEClassFor(Object target) {
        if (target instanceof Invoke) {
            ActionDescriptor[] descriptors = BPELUIRegistry.getInstance().getActionDescriptors();
            for (int i = 0; i < descriptors.length; i++) {
                AbstractBPELAction action = descriptors[i].getAction();
                if (action.isInstanceOf(target)) {
                    return action.getModelType();
                }
            }
        }
        if (!(target instanceof EObject)) {
            return null;
        }
        return ((EObject) target).eClass();
    }

    public static boolean isCustomActivity(Object target) {
        if (target instanceof Invoke) {
            ActionDescriptor[] descriptors = BPELUIRegistry.getInstance().getActionDescriptors();
            for (int i = 0; i < descriptors.length; i++) {
                AbstractBPELAction action = descriptors[i].getAction();
                if (action.getModelType() == BPELPackage.eINSTANCE.getInvoke())
                    continue;
                if (action.isInstanceOf(target)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean isBPELAction(EClass target) {
        ActionDescriptor[] descriptors = BPELUIRegistry.getInstance().getActionDescriptors();
        for (int i = 0; i < descriptors.length; i++) {
            AbstractBPELAction action = descriptors[i].getAction();
            if (action.getModelType() == target) {
                return true;
            }
        }
        return false;
    }

    /**
     * Creates a new instance of clazz using the EFactory of the EPackage clazz belongs to. 
     */
    public static EObject createEObject(EClass clazz) {
        return clazz.getEPackage().getEFactoryInstance().create(clazz);
    }

    // This is a hack to bundle the result of a cloneSubtree with enough state to undo/redo
    // the extension map changes it caused. 
    public static class CloneResult {

        /** The result of the clone */
        public EObject targetRoot;
        Map<EObject, EObject> targetMap;
        Map<EObject, EObject> targetMapAdditions = new HashMap<EObject, EObject>();

        /**
         * Undo ... ?
         */
        public void undo() {
            for (EObject next : targetMapAdditions.keySet()) {
                targetMap.remove(next);
            }
        }

        /**
         * Redo ... ? 
         */
        public void redo() {
            for (EObject key : targetMapAdditions.keySet()) {
                targetMap.put(key, targetMapAdditions.get(key));
            }
        }
    }

    // This helper is used by the cloneSubtree() method.
    protected static void cloneSubtreeHelper(EObject source, Map<EObject, EObject> sourceMap,
            Map<EObject, EObject> targetMap, Map<EObject, EObject> copyMap, CloneResult result) {
        EObject targetObject = createEObject(source.eClass());
        copyMap.put(source, targetObject);

        if (sourceMap != null && sourceMap.containsKey(source)) {

            EObject sourceExtension = sourceMap.get(source);
            EObject targetExtension = createEObject(sourceExtension.eClass());

            copyMap.put(sourceExtension, targetExtension);

            for (TreeIterator<?> it2 = sourceExtension.eAllContents(); it2.hasNext();) {
                EObject source2 = (EObject) it2.next();
                EObject target2 = createEObject(source2.eClass());
                copyMap.put(source2, target2);
            }

            targetMap.put(targetObject, targetExtension);
            result.targetMapAdditions.put(targetObject, targetExtension);
        }
    }

    /**
     * Clones an EObject and all EObjects contained directly or indirectly within it.  All
     * cloned objects possessing an extension in the sourceMap will also have their extensions
     * cloned into the targetMap.  Containment references and other references to any of the
     * cloned object(s) will be fixed up to point into the target objects.  Any references to
     * non-cloned objects will be copied as-is in the cloned objects.
     * 
     * NOTE: This method relies on BPELUtil.createEObject() knowing how to create new instances
     * of the EClasses of all copied objects (i.e. objectFactories must contain the necessary
     * EFactory instances for everything copied by this method).
     * 
     * @param source The root of the source subtree to clone.
     * @param sourceMap The extension map containing source extensions of cloned objects.
     * @param targetMap The extension map in which cloned extensions should be recorded.
     * @return a CloneResult containing the root of the target subtree, which can be used
     * for undo/redo.
     */
    @SuppressWarnings("nls")
    public static CloneResult cloneSubtree(EObject source, Map<EObject, EObject> sourceMap,
            Map<EObject, EObject> targetMap) {

        HashMap<EObject, EObject> copyMap = new HashMap<EObject, EObject>();

        CloneResult result = new CloneResult();
        result.targetMap = targetMap;

        // (1) Create target objects for each EObject in the containment subtree of source.
        // If the source object has an extension in sourceMap, create copies of the extension
        // and its containment tree as well.
        // NOTE: we can NOT just recursively call cloneSubtree for the extension, it wouldn't
        // work with fixing up references.  We have to iterate its eAllContents also here.

        cloneSubtreeHelper(source, sourceMap, targetMap, copyMap, result);

        for (TreeIterator<?> it = source.eAllContents(); it.hasNext();) {
            EObject sourceObject = (EObject) it.next();
            cloneSubtreeHelper(sourceObject, sourceMap, targetMap, copyMap, result);
        }

        // (2) Copy the features from each cloned source object to the corresponding target
        // object.  As we copy, we replace any references to cloned source objects with
        // references to the corresponding target objects--but references to non-cloned
        // objects are copied as-is.

        for (Map.Entry<EObject, EObject> entry : copyMap.entrySet()) {

            EObject sourceObject = entry.getKey();
            EObject targetObject = entry.getValue();

            if (sourceObject.eClass() != targetObject.eClass()) {
                throw new IllegalStateException(
                        "Source and target objects are not of the same class after cloning.");
            }

            if (Policy.DEBUG) {
                System.out.println("copying a " + sourceObject.eClass().getName()); //$NON-NLS-1$
            }

            for (EStructuralFeature feature : sourceObject.eClass().getEAllStructuralFeatures()) {

                // special cases first.
                if (!feature.isChangeable()) {
                    if (Policy.DEBUG)
                        System.out.println("  *** skipping unchangeable feature " + feature); //$NON-NLS-1$
                    continue;
                }

                if (feature.isUnsettable() && !targetObject.eIsSet(feature)) {
                    if (Policy.DEBUG)
                        System.out.println("  unsetting feature " + feature.getName()); //$NON-NLS-1$
                    targetObject.eUnset(feature);
                    continue;
                }

                Object value = sourceObject.eGet(feature);

                boolean treatAsReference = (feature instanceof EReference);

                if (treatAsReference) {
                    if (feature.isMany()) {
                        // list of references.
                        EList<Object> newValues = new BasicEList<Object>();
                        if (Policy.DEBUG)
                            System.out.println("  copying multi-reference feature " + feature.getName() + ":"); //$NON-NLS-1$ //$NON-NLS-2$

                        for (Iterator<?> it3 = ((Collection) value).iterator(); it3.hasNext();) {
                            Object oldValue = it3.next();
                            Object newValue = (oldValue == null ? null : copyMap.get(oldValue));

                            if (newValue == null) {
                                newValue = oldValue;
                            }
                            if (Policy.DEBUG)
                                System.out.println("+ (oldValue=" + oldValue + " newValue=" + newValue + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                            newValues.add(newValue);
                        }
                        targetObject.eSet(feature, newValues);
                    } else {
                        // single reference.
                        Object newValue = (value == null ? null : copyMap.get(value));
                        if (newValue == null) {
                            newValue = value;
                        }
                        if (Policy.DEBUG)
                            System.out.println("  copying reference feature " + feature.getName() //$NON-NLS-1$
                                    + " (value=" + value + " newValue=" + newValue + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                        targetObject.eSet(feature, newValue);
                    }
                } else {

                    /** In case of a DOM Node and the "element" feature, we simply clone the result */
                    if (value instanceof org.w3c.dom.Node && "element".equals(feature.getName())) {
                        org.w3c.dom.Node e = (org.w3c.dom.Node) value;
                        value = e.cloneNode(true);
                    }

                    // non-reference attribute.  just copy the value
                    if (Policy.DEBUG)
                        System.out
                                .println("  copying attr feature " + feature.getName() + " (value=" + value + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    targetObject.eSet(feature, value);
                }
            }
        }

        result.targetRoot = copyMap.get(source);
        return result;
    }

    /**
     * Convenience formatting methods.
     */
    public static String formatString(String format, String arg1) {
        return MessageFormat.format(format, new Object[] { arg1 });
    }

    public static String formatString(String format, String arg1, String arg2) {
        return MessageFormat.format(format, new Object[] { arg1, arg2 });
    }

    /**
     * strips out invalid characters to conform to QName specs.
     * If the resulting name is null, returns "bpel" as a valid QName
     * to guarantee that something valid is returned.
     * 
     * TODO: This has to be a valid NCName ...
     * 
     * @param str
     *  
     * @return 
     */

    public static String generateValidName(String str) {

        StringBuilder result = new StringBuilder(""); //$NON-NLS-1$
        if (str != null) {
            for (char ch : str.trim().toCharArray()) {
                int destLength = result.length();
                if (((destLength == 0) && (Character.isLetter(ch) || ch == '_'))
                        || ((destLength > 0) && Character.isJavaIdentifierPart(ch))) {
                    result.append(ch);
                }
            }
        }

        if (result.length() == 0)
            result.append(IBPELUIConstants.EXTENSION_BPEL);

        return result.toString();
    }

    /**
     * Helper that traverses the IContainer hierarchy of the given modelObject in depth
     * first fashion and applies the given visitor to each node.
     * 
     * DO NOT USE THIS for anything that must see "all" model objects (including implicit
     * sequences, for example).  Use TreeIterator modelObject.eAllContents() for that.
     */
    public static void visitModelDepthFirst(Object modelObject, IModelVisitor visitor) {
        if (visitor.visit(modelObject)) {
            IContainer container = BPELUtil.adapt(modelObject, IContainer.class);
            if (container != null) {
                for (Iterator it = container.getChildren(modelObject).iterator(); it.hasNext();) {
                    visitModelDepthFirst(it.next(), visitor);
                }
            }
            // TODO: Make this go away
            if (modelObject instanceof Flow) {
                // Hack: also visit the links of a flow!
                Flow flow = (Flow) modelObject;
                for (Iterator it = FlowLinkUtil.getFlowLinks(flow).iterator(); it.hasNext();) {
                    visitModelDepthFirst(it.next(), visitor);
                }
            }
        }
    }

    private static class NameUnusedVisitor implements IModelVisitor {
        private boolean unused = true;
        private String candidateName;
        private Collection<EObject> ignoreObjects;

        NameUnusedVisitor(String candidateName, Collection<EObject> ignoreObjects) {
            this.candidateName = candidateName;
            if (ignoreObjects == null)
                ignoreObjects = Collections.emptySet();
            this.ignoreObjects = ignoreObjects;
        }

        public boolean visit(Object child) {
            if (!ignoreObjects.contains(child)) {
                INamedElement namedElement = BPELUtil.adapt(child, INamedElement.class);
                if (namedElement != null) {
                    String name = namedElement.getName(child);
                    if ((name != null) && (name.compareToIgnoreCase(candidateName) == 0))
                        unused = false;
                }
            }
            return true;//unused;
        }

        public boolean isUnused() {
            return unused;
        }
    }

    /** 
     * checks if a name is available for use within the given process (i.e. if this name
     * were added within the modelRoot, would it be unique).  
     */
    public static boolean isNameUnused(EObject modelRoot, String candidateName, Collection ignoreObjects) {
        NameUnusedVisitor visitor = new NameUnusedVisitor(candidateName, ignoreObjects);
        for (TreeIterator<EObject> it = modelRoot.eAllContents(); it.hasNext();) {
            visitor.visit(it.next());
            if (visitor.isUnused() == false)
                return false;
        }
        return true;
    }

    /**
     * return a mangled name (based on the given hint) which is unique in the given process.
     */
    public static String getUniqueModelName(EObject context, String hint, Collection ignoreObjects) {
        return getUniqueModelName2(context, hint, ignoreObjects);
    }

    /**
     * return a mangled name (based on the given hint) which is unique in the given WSDL definition.
     */
    public static String getUniqueModelName(Definition definition, String hint, Collection ignoreObjects) {
        return getUniqueModelName2(definition, hint, ignoreObjects);
    }

    protected static String getUniqueModelName2(EObject modelRoot, String hint, Collection ignoreObjects) {

        // first try it exactly as hinted.
        String result = BPELUtil.generateValidName((hint == null) ? "" : hint.trim()); //$NON-NLS-1$
        if (isNameUnused(modelRoot, result, ignoreObjects))
            return result;

        // go back to the first non-digit
        int digitPos = result.length() - 1;
        while (digitPos >= 0 && Character.isDigit(result.charAt(digitPos)))
            digitPos--;
        digitPos++; // move back to the digit
        String nameWithoutNum = result.substring(0, digitPos);

        // try increasing numbers until one is accepted.
        for (int num = 1;; num++) {
            result = nameWithoutNum + String.valueOf(num);
            if (isNameUnused(modelRoot, result, ignoreObjects))
                return result;
        }
    }

    public static String generateUniqueModelName(EObject context, String hint, EObject model) {

        if (hint == null || "".equals(hint)) { //$NON-NLS-1$
            ILabeledElement element = BPELUtil.adapt(model, ILabeledElement.class);
            hint = (element != null) ? element.getTypeLabel(model) : ""; //$NON-NLS-1$
        }
        return BPELUtil.getUniqueModelName(context, hint, Collections.singletonList(model));
    }

    public static String getFilenameFromUri(String uri) {
        if (uri == null)
            return Messages.BPELUtil__unknown_URI__54;
        // Hack. Why aren't we just using URI objects?
        int idx = Math.max(uri.lastIndexOf("/"), uri.lastIndexOf("\\")); //$NON-NLS-1$ //$NON-NLS-2$
        return (idx >= 0) ? uri.substring(idx + 1) : uri;
    }

    /**
     * Converts the first letter of the target String to upper case.
     * @param target 
     * @return the name with the first letter uppercased.
     */
    public static String upperCaseFirstLetter(String target) {
        if (target.length() < 1) {
            return target;
        }
        StringBuilder buf = new StringBuilder(target.length());
        buf.append(target.substring(0, 1).toUpperCase());
        buf.append(target.substring(1, target.length()));
        return buf.toString();
    }

    /**
     * Converts the first letter of the target String to lower case.
     */
    public static String lowerCaseFirstLetter(String target) {
        if (target.length() < 1) {
            return target;
        }
        StringBuffer buf = new StringBuffer(target.length());
        buf.append(target.substring(0, 1).toLowerCase());
        buf.append(target.substring(1, target.length()));
        return buf.toString();
    }

    /**
     * Returns all of the PropertyAlias objects from WSDL files in the same ResourceSet as
     * the resource containing messageType, which are aliases for messageType.
     */
    public static List<PropertyAlias> getPropertyAliasesForMessageType(Message messageType) {
        List<PropertyAlias> aliases = new ArrayList<PropertyAlias>();
        Resource resource = messageType.eResource();
        if (resource == null) {
            return aliases;
        }
        ResourceSet resourceSet = resource.getResourceSet();
        for (Iterator<Resource> it = resourceSet.getResources().iterator(); it.hasNext();) {
            resource = it.next();
            // TODO: this is a hack.  Why is there no WSDLResource interface??
            if (resource instanceof WSDLResourceImpl) {
                for (TreeIterator<EObject> treeIt = resource.getAllContents(); treeIt.hasNext();) {
                    EObject object = treeIt.next();
                    if (object instanceof PropertyAlias) {
                        PropertyAlias alias = (PropertyAlias) object;
                        if (messageType.equals(alias.getMessageType()))
                            aliases.add(alias);
                    }
                }
            }
        }
        return aliases;
    }

    // Creates an implicit sequence with a name that is unique in the editor's process.
    // Note that an ActivityExtension is created and inserted in the extension map,
    // but the implicit sequence itself should be inserted in the model by the caller.
    public static Sequence createImplicitSequence(Process process, ExtensionMap extensionMap) {
        Sequence impSeq = BPELFactory.eINSTANCE.createSequence();
        ModelHelper.createExtensionIfNecessary(extensionMap, impSeq);
        Collection ignoreObjects = Collections.singletonList(impSeq);
        if (ModelHelper.isSpecCompliant(process)) {
            impSeq.setName(getUniqueModelName(process, Messages.BPELUtil_Sequence_1, ignoreObjects));
        } else {
            impSeq.setName(getUniqueModelName(process, Messages.BPELUtil_HiddenSequence_2, ignoreObjects));
            ((ActivityExtension) ModelHelper.getExtension(impSeq)).setImplicit(true);
        }
        // TODO: also give sequence a unique ID marked as implicit!
        return impSeq;
    }

    public static TreeIterator nodeAndAllContents(final EObject node) {
        final TreeIterator<EObject> allContents = node.eAllContents();
        return new TreeIterator() {
            boolean didNode = false;

            public void prune() {
                // TODO: This won't work when calling on the first item.
                if (!didNode)
                    throw new IllegalStateException();
                allContents.prune();
            }

            public boolean hasNext() {
                if (didNode)
                    return allContents.hasNext();
                return node != null;
            }

            public Object next() {
                if (didNode)
                    return allContents.next();
                didNode = true;
                return node;
            }

            public void remove() {
                // This won't work when calling on the first item.
                if (!didNode)
                    throw new IllegalStateException();
                allContents.remove();
            }
        };
    }

    private static class RefreshActionVisitor implements IModelVisitor {
        private GraphicalViewer viewer;

        public RefreshActionVisitor(GraphicalViewer viewer) {
            this.viewer = viewer;
        }

        public boolean visit(Object child) {
            EditPart ep = (EditPart) viewer.getEditPartRegistry().get(child);
            if (ep != null && ep instanceof BPELEditPart) {
                IFigure fig = ((BPELEditPart) ep).getContentPane();
                if (fig != null) {
                    ((BPELEditPart) ep).regenerateVisuals();
                    ep.refresh();
                }
            }
            if (ep instanceof LinkEditPart) {
                ep.refresh();
            }
            return true;//unused;
        }
    }

    /** 
     * refreshes all the editparts of the process. Useful for changing layouts etc
     */
    public static void regenerateVisuals(Process process, GraphicalViewer viewer) {
        RefreshActionVisitor visitor = new RefreshActionVisitor(viewer);
        visitModelDepthFirst(process, visitor);
        return;
    }

    /**
     * The policy for whether a BPELEditPart's edges should be hilighted or not.  This one defers
     * to the active tool if it is an IHilightControllingTool and says no otherwise.
     */
    public static boolean shouldHilightEdges(EditDomain domain, EObject modelObject) {
        Tool tool = domain.getActiveTool();
        if (tool instanceof IHilightControllingTool) {
            return ((IHilightControllingTool) tool).hilightModelTarget(modelObject);
        }
        return false;
    }

    /**
     * Used to determine the type of pattern to paint a container in the Process.
     * Because the nesting of containers is confusing, we want to draw nice gradients
     * to help the user.
     * 1 and 3 return values mean solid fill.
     * 0 and 2 mean gradient fills.
     */
    public static int getRepaintFillType(IFigure fig) {
        int depth = 0;
        IFigure parent = fig.getParent();
        while (parent != null) {
            if (parent != null && parent.getBorder() != null && parent.getBorder() instanceof GradientBorder) {
                depth++;
            }
            parent = parent.getParent();
        }
        return depth % 4;
    }

    public static void sortFlowList(List<FlowEditPart> listOfFlowEditParts) {
        List<FlowEditPart> result = listOfFlowEditParts;
        int resultSize = result.size();

        for (int i = 0; i < resultSize; i++) {
            for (int j = i + 1; j < resultSize; j++) {
                Flow flow1 = (Flow) (result.get(i)).getModel();
                Flow flow2 = (Flow) (result.get(j)).getModel();
                Flow[] parents = FlowLinkUtil.getParentFlows(flow2);
                for (int k = 0; k < parents.length; k++) {
                    if (parents[k] == flow1) {
                        // flow2 must be layed out before flow1 so its size will be known!
                        FlowEditPart temp = result.get(i);
                        result.set(i, result.get(j));
                        result.set(j, temp);
                    }
                }
            }
        }
    }

    /**
     * Refresh the given ComboViewer, and also make sure selectedObject is selected in it.
     */
    public static void refreshCombo(ComboViewer viewer, Object selectedObject) {
        viewer.refresh();
        String s = ((ILabelProvider) viewer.getLabelProvider()).getText(selectedObject);
        viewer.getCombo().setText(s);
    }

    /**
     * Helper method to calculate the width of a button.
     * This is necessary for internationalization and accessibility.
     * Returned value is the calculated width or defaultSize, whichever
     * is larger.
     */
    public static int calculateButtonWidth(Widget widget, int defaultSize) {
        GC gc;
        int width = 0;

        if (widget instanceof Button) {
            Button w = (Button) widget;
            gc = new GC(w);
            gc.setFont(w.getFont());
            width = gc.textExtent(w.getText()).x + 17;
            gc.dispose();
            return Math.max(width, defaultSize);
        }
        return defaultSize;
    }

    public static String getMaxLengthString(String strings[]) {
        int max = -1;
        int index = -1;
        for (int i = 0; i < strings.length; i++) {
            if (strings[i].length() > max) {
                max = strings[i].length();
                index = i;
            }
        }

        if (index >= 0)
            return strings[index];
        return ""; //$NON-NLS-1$
    }

    /**
     * Helper method to calculate the width of a CLabel.
     * This is necessary for internationalization and accessibility.
     * 
     * Returned value is the calculated width or defaultSize, whichever
     * is larger.
     */
    public static int calculateLabelWidth(Widget widget, int defaultSize) {
        GC gc;
        int width = 0;

        if (widget instanceof CLabel) {
            CLabel w = (CLabel) widget;
            gc = new GC(w);
            gc.setFont(w.getFont());
            width = gc.textExtent(w.getText()).x + 17;
            gc.dispose();

            return Math.max(width, defaultSize);
        }
        if (widget instanceof DecoratedLabel) {
            DecoratedLabel w = (DecoratedLabel) widget;
            gc = new GC(w);
            gc.setFont(w.getFont());
            width = gc.textExtent(w.getText()).x + 17;
            gc.dispose();
            return Math.max(width, defaultSize);
        }

        if (widget instanceof Label) {
            Label w = (Label) widget;
            gc = new GC(w);
            gc.setFont(w.getFont());
            width = gc.textExtent(w.getText()).x + 5;
            gc.dispose();
            return Math.max(width, defaultSize);
        }
        return defaultSize;
    }

    public static IFile getFileFromURI(URI uri) {
        if (uri.isFile()) {
            return getFileFromDeviceURI(uri);
        }
        return getFileFromPlatformURI(uri);
    }

    public static IFile getFileFromDeviceURI(URI uri) {
        String device = uri.device();
        Iterator pathIt = uri.segmentsList().iterator();
        StringBuffer path = new StringBuffer();
        while (pathIt.hasNext()) {
            path.append("/" + pathIt.next()); //$NON-NLS-1$
        }
        return ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(device, path.toString()));
    }

    public static IFile getFileFromPlatformURI(URI uri) {
        String[] segs = uri.segments();
        IPath path = null;
        // start at 1 to skip resource
        for (int i = 1; i < segs.length; i++) {
            if (path == null) {
                path = new Path(segs[i]);
            } else {
                path = path.append(segs[i]);
            }
        }
        return ResourcesPlugin.getWorkspace().getRoot().getFile(path);
    }

    /** 
     * Function to return a platform URI from a standard hierarchital URI.
     * Normally we can use URI.createPlatformURI, but that function always assumes
     * that it is non-platform
     */
    public static URI getPlatformURI(URI uri) {
        String str = uri.toString();
        if (str.startsWith("platform:")) //$NON-NLS-1$
            return uri;
        return URI.createPlatformResourceURI(uri.toString());
    }

    /* external fault handler helpers */

    public static boolean getShowFaultHandler(EditPart part) {
        if (part instanceof ScopeEditPart)
            return ((ScopeEditPart) part).getShowFaultHandler();
        else if (part instanceof InvokeEditPart)
            return ((InvokeEditPart) part).getShowFaultHandler();
        else if (part instanceof StartNodeEditPart)
            return ((StartNodeEditPart) part).getShowFaultHandler();
        return false;
    }

    public static void setShowFaultHandler(EditPart part, boolean show) {
        if (part instanceof ScopeEditPart)
            ((ScopeEditPart) part).setShowFaultHandler(show);
        else if (part instanceof InvokeEditPart)
            ((InvokeEditPart) part).setShowFaultHandler(show);
        else if (part instanceof StartNodeEditPart)
            ((StartNodeEditPart) part).setShowFaultHandler(show);
    }

    /* external compensation handler helpers */

    public static boolean getShowCompensationHandler(EditPart part) {
        if (part instanceof ScopeEditPart)
            return ((ScopeEditPart) part).getShowCompensationHandler();
        else if (part instanceof InvokeEditPart)
            return ((InvokeEditPart) part).getShowCompensationHandler();
        return false;
    }

    public static boolean getShowTerminationHandler(EditPart part) {
        if (part instanceof ScopeEditPart)
            return ((ScopeEditPart) part).getShowTerminationHandler();
        return false;
    }

    public static void setShowCompensationHandler(EditPart part, boolean show) {
        if (part instanceof ScopeEditPart)
            ((ScopeEditPart) part).setShowCompensationHandler(show);
        else if (part instanceof InvokeEditPart)
            ((InvokeEditPart) part).setShowCompensationHandler(show);
    }

    public static void setShowTerminationHandler(EditPart part, boolean show) {
        if (part instanceof ScopeEditPart)
            ((ScopeEditPart) part).setShowTerminationHandler(show);
    }

    /* external event handler helpers */

    public static boolean getShowEventHandler(EditPart part) {
        if (part instanceof ScopeEditPart)
            return ((ScopeEditPart) part).getShowEventHandler();
        else if (part instanceof StartNodeEditPart)
            return ((StartNodeEditPart) part).getShowEventHandler();
        return false;
    }

    public static void setShowEventHandler(EditPart part, boolean show) {
        if (part instanceof ScopeEditPart)
            ((ScopeEditPart) part).setShowEventHandler(show);
        else if (part instanceof StartNodeEditPart)
            ((StartNodeEditPart) part).setShowEventHandler(show);
    }

    /**
     * Returns the extension file of the given BPEL file.
     */
    public static IFile getBPELEXFile(IFile bpelFile) {
        IPath path = bpelFile.getFullPath().removeFileExtension()
                .addFileExtension(IBPELUIConstants.EXTENSION_MODEL_EXTENSIONS);
        return ResourcesPlugin.getWorkspace().getRoot().getFile(path);
    }

    /**
     * Returns the artifacts WSDL of the given BPEL file.
     */
    public static IFile getArtifactsWSDLFile(IFile bpelFile) {
        IPath wsdlPath = bpelFile.getFullPath().removeFileExtension();
        String fileName = wsdlPath.lastSegment() + "Artifacts"; //$NON-NLS-1$
        wsdlPath = wsdlPath.removeLastSegments(1).append(fileName);
        wsdlPath = wsdlPath.addFileExtension(IBPELUIConstants.EXTENSION_WSDL);
        return ResourcesPlugin.getWorkspace().getRoot().getFile(wsdlPath);
    }

    public static Image getImage(IMarker marker) {
        Image img = ModelMarkerUtil.getImage(marker);
        ImageData background = null;
        if (img != null) {
            background = img.getImageData();
        }
        if (background == null) {
            // Don't give up yet. If this is also a problem marker, we can find an image to
            // display...
            try {
                if (marker.isSubtypeOf(IMarker.PROBLEM)) {
                    background = ImageUtils.getImage(marker).getImageData();
                }
            } catch (CoreException e) {
                BPELUIPlugin.log(e);
                return null;
            }
        }
        if (background == null)
            return null;
        String uri = marker.getAttribute(IBPELUIConstants.MARKER_OVERLAYIMAGETOPLEFT, ""); //$NON-NLS-1$
        ImageData topLeft = getImageData(uri);
        uri = marker.getAttribute(IBPELUIConstants.MARKER_OVERLAYIMAGETOPRIGHT, ""); //$NON-NLS-1$
        ImageData topRight = getImageData(uri);
        uri = marker.getAttribute(IBPELUIConstants.MARKER_OVERLAYIMAGEBOTTOMLEFT, ""); //$NON-NLS-1$
        ImageData bottomLeft = getImageData(uri);
        uri = marker.getAttribute(IBPELUIConstants.MARKER_OVERLAYIMAGEBOTTOMRIGHT, ""); //$NON-NLS-1$
        ImageData bottomRight = getImageData(uri);
        OverlayCompositeImageDescriptor descriptor = new OverlayCompositeImageDescriptor(background, topLeft,
                topRight, bottomLeft, bottomRight);
        return descriptor.createImage();
    }

    private static ImageData getImageData(String uri) {
        if (uri.length() == 0)
            return null;
        URL url = null;
        try {
            url = new URL(uri);
        } catch (MalformedURLException e) {
            return null;
        }
        ImageDescriptor desc = ImageDescriptor.createFromURL(url);
        return desc.getImageData();
    }

    /**
     * Returns the EditPart which is responsible for the given IFigure.
     */
    public static EditPart mapFigure2EditPart(EditPartViewer viewer, IFigure figure) {
        Map visualPartMap = viewer.getVisualPartMap();
        EditPart part = null;
        while (part == null && figure != null) {
            part = (EditPart) visualPartMap.get(figure);
            figure = figure.getParent();
        }
        return part;
    }

    /**
     * Reads the process from disk.
     */
    public static Process getProcess(IResource bpelFile, ResourceSet resourceSet) throws IOException {
        URI uri = URI.createPlatformResourceURI(bpelFile.getFullPath().toString());
        Resource processResource = resourceSet.getResource(uri, true);
        EList<EObject> contents = processResource.getContents();
        if (!contents.isEmpty()) {
            return (Process) contents.get(0);
        }
        return null;
    }

    public static AccessibleEditPart getAccessibleEditPart(GraphicalEditPart part) {
        final GraphicalEditPart thisPart = part;

        return new AccessibleEditPart() {
            @Override
            public void getName(AccessibleEvent e) {
                String childType = null;
                String displayName = null;
                ILabeledElement labeledElement = BPELUtil.adapt(thisPart.getModel(), ILabeledElement.class);
                if (labeledElement != null) {
                    childType = labeledElement.getTypeLabel(thisPart.getModel());
                    displayName = labeledElement.getLabel(thisPart.getModel());
                    if (childType != null && displayName.equals(childType)) {
                        childType = null;
                    }
                } else {
                    e.result = null;
                    return;
                }

                // return something reasonable (type followed by name if any)
                // or nothing at all

                StringBuffer concat = new StringBuffer();
                if (childType != null && childType.length() > 0)
                    concat.append(childType);
                if (concat.length() > 0)
                    concat.append(" "); //$NON-NLS-1$
                if (displayName != null && displayName.length() > 0)
                    concat.append(displayName);
                if (concat.length() > 0)
                    e.result = concat.toString();
                else
                    e.result = null;
                return;
            }

            @Override
            public void getChildCount(AccessibleControlEvent e) {
                List<EditPart> list = thisPart.getChildren();
                int count = 0;
                for (EditPart part : list) {
                    AccessibleEditPart access = (AccessibleEditPart) part.getAdapter(AccessibleEditPart.class);
                    if (access == null)
                        continue;
                    count++;
                }
                e.detail = count;
            }

            @Override
            public void getChildren(AccessibleControlEvent e) {
                List<EditPart> list = thisPart.getChildren();
                Vector<Integer> childList = new Vector<Integer>();
                for (EditPart part : list) {
                    AccessibleEditPart access = (AccessibleEditPart) part.getAdapter(AccessibleEditPart.class);
                    if (access == null)
                        continue;
                    childList.add(new Integer(access.getAccessibleID()));
                }
                e.children = childList.toArray();
            }

            @Override
            public void getLocation(AccessibleControlEvent e) {
                Rectangle bounds = thisPart.getFigure().getBounds().getCopy();
                thisPart.getFigure().translateToAbsolute(bounds);
                org.eclipse.swt.graphics.Point p = new org.eclipse.swt.graphics.Point(0, 0);
                p = thisPart.getViewer().getControl().toDisplay(p);
                e.x = bounds.x + p.x;
                e.y = bounds.y + p.y;
                e.width = bounds.width;
                e.height = bounds.height;
            }

            /**
             * @see AccessibleEditPart#getState(AccessibleControlEvent)
             */
            @Override
            public void getState(AccessibleControlEvent e) {
                e.detail = ACC.STATE_SELECTABLE | ACC.STATE_FOCUSABLE;
                if (thisPart.getSelected() != EditPart.SELECTED_NONE)
                    e.detail |= ACC.STATE_SELECTED;
                if (thisPart.getViewer().getFocusEditPart() == thisPart)
                    e.detail = ACC.STATE_FOCUSED;
            }
        };
    }

    /** creates a table cursor that can be used to navigate tables for keyboard accessibility **/

    public static TableCursor createTableCursor(final Table table, final TableViewer tableViewer) {
        // create a TableCursor to navigate around the table
        final TableCursor cursor = new TableCursor(table, SWT.NONE);
        cursor.addSelectionListener(new SelectionAdapter() {
            // when the TableEditor is over a cell, select the corresponding row in the table
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (cursor.getRow() != null)
                    table.setSelection(new TableItem[] { cursor.getRow() });
            }

            // when the user hits "ENTER" in the TableCursor, pop up an editor
            @Override
            public void widgetDefaultSelected(SelectionEvent e) {
                TableItem row = cursor.getRow();
                if (row != null) {
                    int nRow = table.indexOf(row);
                    int column = cursor.getColumn();
                    Object obj = tableViewer.getElementAt(nRow);
                    tableViewer.editElement(obj, column);
                }
            }
        });

        // Hide the TableCursor when the user hits the "CTRL" or "SHIFT" key.
        // This alows the user to select multiple items in the table.
        cursor.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if ((e.keyCode == SWT.CTRL) || (e.keyCode == SWT.SHIFT) || (e.stateMask & SWT.CONTROL) != 0
                        || (e.stateMask & SWT.SHIFT) != 0) {
                    cursor.setVisible(false);
                }
            }
        });

        cursor.addMouseListener(new MouseListener() {
            public void mouseDoubleClick(MouseEvent e) {
            }

            public void mouseDown(MouseEvent e) {
                TableItem row = cursor.getRow();
                if (row != null) {
                    int nRow = table.indexOf(row);
                    int column = cursor.getColumn();
                    Object obj = tableViewer.getElementAt(nRow);
                    tableViewer.editElement(obj, column);
                }
            }

            public void mouseUp(MouseEvent e) {
            }
        });

        // Show the TableCursor when the user releases the "SHIFT" or "CTRL" key.
        // This signals the end of the multiple selection task.
        table.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if (e.keyCode == SWT.CONTROL && (e.stateMask & SWT.SHIFT) != 0)
                    return;
                if (e.keyCode == SWT.SHIFT && (e.stateMask & SWT.CONTROL) != 0)
                    return;
                if (e.keyCode != SWT.CONTROL && (e.stateMask & SWT.CONTROL) != 0)
                    return;
                if (e.keyCode != SWT.SHIFT && (e.stateMask & SWT.SHIFT) != 0)
                    return;

                if (table.getItemCount() == 0)
                    return;
                TableItem[] selection = table.getSelection();
                TableItem row = (selection.length == 0) ? table.getItem(table.getTopIndex()) : selection[0];
                table.showItem(row);
                cursor.setSelection(row, 0);
                cursor.setVisible(true);
                cursor.setFocus();
            }
        });
        return cursor;
    }

    public static ResourceSet createResourceSetImpl() {
        // TODO: Extensibility
        return new ResourceSetImpl();
    }

    static final NCNameWordDetector NCNAME_DETECTOR = new NCNameWordDetector();

    /**
     * Returns a validator that checks that the new value is a valid NCName.
     */
    public static IInputValidator getNCNameValidator() {
        return new IInputValidator() {
            public String isValid(String newText) {
                if (NCNAME_DETECTOR.isValid(newText) == false) {
                    return Messages.BPELUtil_NCName;
                }
                // TODO ! temporary hack
                return null;
            }
        };
    }

    public static void deleteNonContainmentRefs(EObject modelObject, Collection referents) {
        if (modelObject == null)
            return;
        for (EReference feature : modelObject.eClass().getEAllReferences()) {
            if (feature.isMany()) {
                EList<Object> list = (EList<Object>) modelObject.eGet(feature, true);
                for (Object referent : referents) {
                    if (list.contains(referent))
                        list.remove(referent);
                    // TODO: support non-changeable features!  print a warning.
                }
            } else {
                Object oldValue = modelObject.eGet(feature, true);
                for (Object referent : referents) {
                    if (oldValue == referent) {
                        if (feature.isUnsettable()) {
                            // this is okay, default is always null for EReferences.
                            modelObject.eUnset(feature);
                        } else {
                            modelObject.eSet(feature, null);
                        }
                        break;
                    }
                    // TODO: support non-changeable features!  print a warning.
                }
            }
        }
    }

    //@return:  returns arraylist with all activities the compensate
    //         can validly point to
    public static ArrayList getCompensableActivities(Object context) {
        final ArrayList returnObjects = new ArrayList();
        if (context instanceof CompensateScope) {
            CompensateScope compensateScope = (CompensateScope) context;
            EObject enclosingContainer = compensateScope;
            if (compensateScope.eContainer() != null) {
                enclosingContainer = enclosingContainer.eContainer();
                // Go to parent scope where compensate is contained
                while (!(enclosingContainer instanceof Scope) && (enclosingContainer.eContainer() != null)) {
                    enclosingContainer = enclosingContainer.eContainer();
                }
            }

            // Put all scopes and invokes within parent scope in arraylist
            visitModelDepthFirst(enclosingContainer, new IModelVisitor() {
                public boolean visit(Object modelObject) {
                    if (modelObject instanceof Scope) {
                        returnObjects.add(modelObject);
                    } else if (modelObject instanceof Invoke) {
                        returnObjects.add(modelObject);
                    }
                    return true;
                }
            });
            returnObjects.remove(0); //remove the scope containing the compensate         
            return returnObjects;
        }
        throw new IllegalArgumentException();
    }

    public static Object resolveXSDObject(Object xsdObject) {
        if (xsdObject instanceof XSDElementDeclaration) {
            XSDElementDeclaration resolvedElement = ((XSDElementDeclaration) xsdObject)
                    .getResolvedElementDeclaration();
            if (resolvedElement != null)
                xsdObject = resolvedElement;
        } else if (xsdObject instanceof XSDAttributeDeclaration) {
            XSDAttributeDeclaration resolvedAttribute = ((XSDAttributeDeclaration) xsdObject)
                    .getResolvedAttributeDeclaration();
            if (resolvedAttribute != null)
                xsdObject = resolvedAttribute;
        }
        return xsdObject;
    }

    public static String debugObject(Object object) {
        if (object == null)
            return "null"; //$NON-NLS-1$
        if (object instanceof String) {
            return "\"" + (String) object + "\""; //$NON-NLS-1$//$NON-NLS-2$
        }
        if (object.getClass().getName().startsWith("java.lang")) { //$NON-NLS-1$
            return object.toString();
        }
        if (object instanceof List) {
            StringBuffer b = new StringBuffer("("); //$NON-NLS-1$
            for (Iterator it = ((List) object).iterator(); it.hasNext();) {
                b.append(debugObject(it.next()));
                if (it.hasNext())
                    b.append(", "); //$NON-NLS-1$
            }
            b.append(")"); //$NON-NLS-1$
            return b.toString();
        }
        if (object instanceof QName) {
            QName qname = (QName) object;
            return "QName(\"" + qname.getNamespaceURI() + "\", \"" + qname.getLocalPart() + "\")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }
        StringBuffer b = new StringBuffer(shortClassName(object.getClass()));
        boolean proxy = (object instanceof EObject) && ((EObject) object).eIsProxy();
        if (proxy)
            b.append("-proxy"); //$NON-NLS-1$
        boolean isEObject = (object instanceof EObject);
        INamedElement namedElement = null;
        if (isEObject) {
            namedElement = BPELUtil.adapt(object, INamedElement.class);
            if (namedElement != null) {
                try {
                    String s = namedElement.getName(object);
                    b.append((s == null) ? "<null>" : "<\"" + s + "\">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                } catch (Exception e) {
                    b.append("<???>"); //$NON-NLS-1$
                }
            }
        }
        if (namedElement == null) {
            b.append("{"); //$NON-NLS-1$
            b.append(String.valueOf(object.hashCode()));
            b.append("}"); //$NON-NLS-1$
        }
        return b.toString();
    }

    public static String debug(Notification n) {
        StringBuffer b = new StringBuffer(shortClassName(n.getClass()));
        b.append("("); //$NON-NLS-1$
        b.append(debugObject(n.getNotifier()));
        b.append(", "); //$NON-NLS-1$
        switch (n.getEventType()) {
        case Notification.SET - 1:
            b.append("CREATE"); //$NON-NLS-1$
            break;
        case Notification.SET:
            b.append("SET"); //$NON-NLS-1$
            break;
        case Notification.UNSET:
            b.append("UNSET"); //$NON-NLS-1$
            break;
        case Notification.ADD:
            b.append("ADD"); //$NON-NLS-1$
            break;
        case Notification.REMOVE:
            b.append("REMOVE"); //$NON-NLS-1$
            break;
        case Notification.ADD_MANY:
            b.append("ADD_MANY"); //$NON-NLS-1$
            break;
        case Notification.MOVE:
            b.append("MOVE"); //$NON-NLS-1$
            break;
        case Notification.REMOVING_ADAPTER:
            b.append("REMOVING_ADAPTER"); //$NON-NLS-1$
            break;
        case Notification.RESOLVE:
            b.append("RESOLVE"); //$NON-NLS-1$
            break;
        default:
            b.append("??? (" + String.valueOf(n.getEventType()) + ")"); //$NON-NLS-1$ //$NON-NLS-2$
        }
        b.append(" "); //$NON-NLS-1$
        EStructuralFeature feature = (EStructuralFeature) n.getFeature();
        if (feature == null)
            b.append("???"); //$NON-NLS-1$
        else
            b.append(feature.getName());
        if (n.getPosition() >= 0) {
            b.append("["); //$NON-NLS-1$
            b.append(String.valueOf(n.getPosition()));
            b.append("]"); //$NON-NLS-1$
        } else {
            if (feature != null && feature.isMany())
                b.append("{***}"); //$NON-NLS-1$ 
        }
        b.append(": "); //$NON-NLS-1$
        b.append(debugObject(n.getOldValue()));
        b.append(" --> "); //$NON-NLS-1$
        b.append(debugObject(n.getNewValue()));
        b.append(")"); //$NON-NLS-1$
        return b.toString();

    }

    protected static String shortClassName(Class clazz) {
        StringBuffer b = new StringBuffer(clazz.getName());
        for (int i = b.indexOf("."); i >= 0; i = b.indexOf(".")) //$NON-NLS-1$//$NON-NLS-2$
            b.delete(0, i + 1);
        //if (b.indexOf("Impl") == b.length()-4) b.delete(b.length()-4, b.length()));
        return b.toString();
    }

    /**
     * Creates a composite with a flat border around it.
     */
    public static Composite createBorderComposite(Composite parent, TabbedPropertySheetWidgetFactory wf) {
        final Composite result = wf.createComposite(parent);
        FillLayout layout = new FillLayout();
        final int margin = 1;
        layout.marginHeight = margin;
        layout.marginWidth = margin;
        result.setLayout(layout);
        result.addPaintListener(new PaintListener() {
            public void paintControl(PaintEvent e) {
                org.eclipse.swt.graphics.Rectangle bounds = result.getBounds();
                bounds.x = margin - 1;
                bounds.y = margin - 1;
                bounds.width = bounds.width - (margin * 2) + 1;
                bounds.height = bounds.height - (margin * 2) + 1;
                e.gc.drawRectangle(bounds);
            }
        });
        return result;
    }

    static void addVariablesToMap(Map<String, Variable> targetMap, Variables vars, Variable refVar) {
        if (vars == null) {
            return;
        }
        for (Variable v : vars.getChildren()) {
            // scoping for initialization (only visible from).
            if (v == refVar) {
                break;
            }
            if (v.getName() != null) {
                targetMap.put(v.getName(), v);
            }
        }
    }

    static void addVisibleVariables(Map<String, Variable> targetMap, EObject target, Variable refVariable) {
        if (target == null) {
            return;
        }
        if (target instanceof Resource) {
            return;
        }

        if (target instanceof Process) {
            addVariablesToMap(targetMap, ((Process) target).getVariables(), refVariable);
            return;
        }
        // recursively add less local variables first
        addVisibleVariables(targetMap, target.eContainer(), refVariable);

        if (target instanceof Scope) {
            addVariablesToMap(targetMap, ((Scope) target).getVariables(), refVariable);
        }
        if (target instanceof Catch) {
            Variable v = ((Catch) target).getFaultVariable();
            if (v != null && v.getName() != null) {
                targetMap.put(v.getName(), v);
            }
        }
        if (target instanceof OnEvent) {
            Variable v = ((OnEvent) target).getVariable();
            if (v != null && v.getName() != null) {
                targetMap.put(v.getName(), v);
            }
        }

        if (target instanceof ForEach) {
            Variable v = ((ForEach) target).getCounterName();
            if (v != null && v.getName() != null) {
                targetMap.put(v.getName(), v);
            }
        }
    }

    private static void addPartnerLinksToMap(Map<String, PartnerLink> targetMap, PartnerLinks plinks) {
        if (plinks == null)
            return;
        for (PartnerLink p : plinks.getChildren()) {
            if (p.getName() != null)
                targetMap.put(p.getName(), p);
        }
    }

    private static void addVisiblePartnerLinks(Map<String, PartnerLink> targetMap, EObject target) {
        if (target == null)
            return;
        if (target instanceof Resource)
            return;
        if (target instanceof Process) {
            addPartnerLinksToMap(targetMap, ((Process) target).getPartnerLinks());
        } else {
            // recursively add less local partnerlinks first
            addVisiblePartnerLinks(targetMap, target.eContainer());
            if (target instanceof Scope) {
                addPartnerLinksToMap(targetMap, ((Scope) target).getPartnerLinks());
            }
        }
    }

    private static void addCorrelationSetsToMap(Map<String, CorrelationSet> targetMap, CorrelationSets csets) {
        if (csets == null)
            return;
        for (Iterator<CorrelationSet> it = csets.getChildren().iterator(); it.hasNext();) {
            CorrelationSet c = it.next();
            if (c.getName() != null)
                targetMap.put(c.getName(), c);
        }
    }

    private static void addVisibleCorrelationSets(Map<String, CorrelationSet> targetMap, EObject target) {
        if (target == null)
            return;
        if (target instanceof Resource)
            return;
        if (target instanceof Process) {
            addCorrelationSetsToMap(targetMap, ((Process) target).getCorrelationSets());
        } else {
            // recursively add less local correlationsets first
            addVisibleCorrelationSets(targetMap, target.eContainer());
            if (target instanceof Scope) {
                addCorrelationSetsToMap(targetMap, ((Scope) target).getCorrelationSets());
            }
        }
    }

    /**
     * Look up the variables visible to a certain context activity (or the whole process).
     * Variables in BPEL follow lexical scoping rules (resolved OASIS issue 101).
     * 
     * The returned variables are in no particular order.
     */
    public static Variable[] getVisibleVariables(EObject target) {

        Map<String, Variable> name2Variable = new HashMap<String, Variable>();

        addVisibleVariables(name2Variable, target, target instanceof Variable ? (Variable) target : null);

        if (name2Variable.isEmpty()) {
            return EMPTY_VARIABLE_ARRAY;
        }

        Collection<Variable> variables = name2Variable.values();
        if (variables.size() == 1) {
            return variables.toArray(EMPTY_VARIABLE_ARRAY);
        }
        ArrayList<Variable> list = new ArrayList<Variable>(variables);
        Collections.sort(list, new Comparator<Variable>() {
            public int compare(Variable o1, Variable o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        return list.toArray(EMPTY_VARIABLE_ARRAY);
    }

    /**
     * Look up the PartnerLinks visible to a certain context activity (or the whole process).
     * When local PartnerLinks are added to the spec, they will follow lexical scoping rules
     * just like variables.
     * 
     * The returned PartnerLinks are in no particular order.
     */
    public static PartnerLink[] getVisiblePartnerLinks(EObject target) {
        Map<String, PartnerLink> name2PartnerLink = new HashMap<String, PartnerLink>();
        addVisiblePartnerLinks(name2PartnerLink, target);
        if (name2PartnerLink.isEmpty())
            return EMPTY_PARTNERLINK_ARRAY;
        PartnerLink[] result = new PartnerLink[name2PartnerLink.size()];
        name2PartnerLink.values().toArray(result);
        return result;
    }

    /**
     * Look up the PartnerLinks visible to a certain context activity (or the whole process).
     * When local PartnerLinks are added to the spec, they will follow lexical scoping rules
     * just like variables.
     * 
     * The returned PartnerLinks are in no particular order.
     */
    public static CorrelationSet[] getVisibleCorrelationSets(EObject target) {
        Map<String, CorrelationSet> name2CorrelationSet = new HashMap<String, CorrelationSet>();
        addVisibleCorrelationSets(name2CorrelationSet, target);
        if (name2CorrelationSet.isEmpty())
            return EMPTY_CORRELATIONSET_ARRAY;
        CorrelationSet[] result = new CorrelationSet[name2CorrelationSet.size()];
        name2CorrelationSet.values().toArray(result);
        return result;
    }

    /**
     * If the given message is used by an operation in the same definition,
     * returns the Operation that uses the given message.  
     * Otherwise, returns null. 
     */
    public static Operation getOperationFromMessage(Message message) {
        if (message == null)
            return null;
        Definition def = message.getEnclosingDefinition();
        if (def == null)
            return null;
        Iterator<PortType> ptIt = def.getEPortTypes().iterator();
        while (ptIt.hasNext()) {
            PortType pt = ptIt.next();
            Iterator<Operation> it = pt.getOperations().iterator();
            while (it.hasNext()) {
                Operation op = it.next();
                Input input = op.getEInput();
                if (input != null) {
                    if (input.getMessage().getQName().equals(message.getQName())) {
                        return op;
                    }
                }
                Output output = op.getEOutput();
                if (output != null) {
                    if (output.getMessage().getQName().equals(message.getQName())) {
                        return op;
                    }
                }
                Iterator<Fault> faultIterator = op.getEFaults().iterator();
                while (faultIterator.hasNext()) {
                    Fault fault = faultIterator.next();
                    Message faultMessage = fault.getEMessage();
                    if (faultMessage != null) {
                        if (faultMessage.getQName() != null) {
                            if (faultMessage.getQName().equals(message.getQName())) {
                                return op;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    public static void openEditor(EObject modelObject, BPELEditor editor) {
        try {
            Assert.isNotNull(modelObject);
            Assert.isNotNull(modelObject.eResource());
            IFile file = BPELUtil.getFileFromURI(modelObject.eResource().getURI());
            IDE.openEditor(editor.getSite().getWorkbenchWindow().getActivePage(), file);
        } catch (PartInitException ex) {
            BPELUIPlugin.log(ex, IStatus.WARNING);
        }
    }

    /**
     * Returns the BPEL file associated with the given process.
     */
    public static IFile getBPELFile(Process process) {
        return getFileFromURI(process.eResource().getURI());
    }

    public static String lookupOrCreateNamespacePrefix(EObject context, String namespace, String prefix,
            Shell shell) {

        String nsPrefix = BPELUtils.getNamespacePrefix(context, namespace);
        if (nsPrefix != null && nsPrefix.length() > 0) {
            return nsPrefix;
        }

        NamespaceMappingDialog dialog = new NamespaceMappingDialog(shell, context);
        dialog.setNamespace(namespace);
        if (prefix != null) {
            dialog.setPrefix(prefix);
        }

        if (dialog.open() == Window.CANCEL) {
            return nsPrefix;
        }

        nsPrefix = dialog.getPrefix();
        BPELUtils.setNamespacePrefix(context, namespace, nsPrefix);
        return nsPrefix;
    }

    /**
     * Traverses the root object and returns all objects under it that are of the same 
     * class or subclasses of "target".
     */
    public static List<EObject> getAllEObjectsOfType(EObject root, EClass eClass) {
        List<EObject> allElems = new ArrayList<EObject>();
        for (TreeIterator<EObject> iter = root.eAllContents(); iter.hasNext();) {
            EObject element = iter.next();
            if (eClass.isSuperTypeOf(element.eClass()) || element.eClass() == eClass) {
                allElems.add(element);
            }
        }
        return allElems;
    }

    /**
     * Look up the reference variables visible to a certain context activity (or the whole process).
     * Variables in BPEL follow lexical scoping rules (resolved OASIS issue 101).
     * 
     * The returned variables are in no particular order.
     */
    public static ReferenceVariable[] getVisibleReferenceVariables(EObject target) {

        Map<String, ReferenceVariable> name2Variable = new HashMap<String, ReferenceVariable>();

        addVisibleReferenceVariables(name2Variable, target,
                target instanceof ReferenceVariable ? (ReferenceVariable) target : null);

        if (name2Variable.isEmpty()) {
            return EMPTY_REFERENCE_VARIABLE_ARRAY;
        }

        Collection<ReferenceVariable> variables = name2Variable.values();
        if (variables.size() == 1) {
            return variables.toArray(EMPTY_REFERENCE_VARIABLE_ARRAY);
        }
        ArrayList<ReferenceVariable> list = new ArrayList<ReferenceVariable>(variables);
        Collections.sort(list, new Comparator<ReferenceVariable>() {
            public int compare(ReferenceVariable o1, ReferenceVariable o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        return list.toArray(EMPTY_REFERENCE_VARIABLE_ARRAY);
    }

    static void addVisibleReferenceVariables(Map<String, ReferenceVariable> targetMap, EObject target,
            ReferenceVariable refVariable) {
        if (target == null) {
            return;
        }
        if (target instanceof Resource) {
            return;
        }

        if (target instanceof Process) {
            addReferenceVariablesToMap(targetMap, ((Process) target).getReferenceVariables(), refVariable);
            return;
        }
        // recursively add less local variables first
        addVisibleReferenceVariables(targetMap, target.eContainer(), refVariable);
    }

    static void addReferenceVariablesToMap(Map<String, ReferenceVariable> targetMap, ReferenceVariables vars,
            ReferenceVariable refVar) {
        if (vars == null) {
            return;
        }
        for (ReferenceVariable v : vars.getChildren()) {
            // scoping for initialization (only visible from).
            if (v == refVar) {
                break;
            }
            if (v.getName() != null) {
                targetMap.put(v.getName(), v);
            }
        }
    }

    public static ReferenceVariable[] getReferenceVariables(EObject target) {

        Map<String, ReferenceVariable> name2Variable = new HashMap<String, ReferenceVariable>();

        addVisibleReferenceVariables(name2Variable, target,
                target instanceof ReferenceVariable ? (ReferenceVariable) target : null);

        if (name2Variable.isEmpty()) {
            return EMPTY_REFERENCE_VARIABLE_ARRAY;
        }

        Collection<ReferenceVariable> variables = name2Variable.values();
        if (variables.size() == 1) {
            return variables.toArray(EMPTY_REFERENCE_VARIABLE_ARRAY);
        }
        ArrayList<ReferenceVariable> list = new ArrayList<ReferenceVariable>(variables);
        Collections.sort(list, new Comparator<ReferenceVariable>() {
            public int compare(ReferenceVariable o1, ReferenceVariable o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        return list.toArray(EMPTY_REFERENCE_VARIABLE_ARRAY);
    }
}