/*******************************************************************************
 * 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.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 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:")) return uri; //$NON-NLS-1$
		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("{"); b.append(String.valueOf(object.hashCode())); b.append("}"); //$NON-NLS-1$ //$NON-NLS-2$
		}
		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"); break; //$NON-NLS-1$
		case Notification.SET: b.append("SET"); break; //$NON-NLS-1$
		case Notification.UNSET: b.append("UNSET"); break; //$NON-NLS-1$
		case Notification.ADD: b.append("ADD"); break; //$NON-NLS-1$
		case Notification.REMOVE: b.append("REMOVE"); break; //$NON-NLS-1$
		case Notification.ADD_MANY: b.append("ADD_MANY"); break; //$NON-NLS-1$
		case Notification.MOVE: b.append("MOVE"); break; //$NON-NLS-1$
		case Notification.REMOVING_ADAPTER: b.append("REMOVING_ADAPTER"); break; //$NON-NLS-1$
		case Notification.RESOLVE: b.append("RESOLVE"); break; //$NON-NLS-1$
		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("???"); else b.append(feature.getName()); //$NON-NLS-1$
		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(".")) b.delete(0,i+1); //$NON-NLS-1$ //$NON-NLS-2$
		//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;
	}	
}