//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 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 implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.edit.process;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.AbstractTreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.CopyCommand.Helper;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.Disposable;
import org.eclipse.emf.edit.provider.ITableItemLabelProvider;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.emf.edit.provider.IWrapperItemProvider;
import org.eclipse.emf.edit.provider.ViewerNotification;
import org.eclipse.epf.library.edit.ICachedChildrenItemProvider;
import org.eclipse.epf.library.edit.IConfigurable;
import org.eclipse.epf.library.edit.IConfigurator;
import org.eclipse.epf.library.edit.IFilter;
import org.eclipse.epf.library.edit.IWrapperItemProviderFactory;
import org.eclipse.epf.library.edit.IWrapperItemProviderFactoryProvider;
import org.eclipse.epf.library.edit.Providers;
import org.eclipse.epf.library.edit.VariabilityInfo;
import org.eclipse.epf.library.edit.command.IActionManager;
import org.eclipse.epf.library.edit.command.IResourceAwareCommand;
import org.eclipse.epf.library.edit.process.command.ActivityAddCommand;
import org.eclipse.epf.library.edit.util.GraphicalData;
import org.eclipse.epf.library.edit.util.PredecessorList;
import org.eclipse.epf.library.edit.util.ProcessUtil;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.BreakdownElement;
import org.eclipse.epf.uma.Descriptor;
import org.eclipse.epf.uma.Iteration;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.ProcessComponent;
import org.eclipse.epf.uma.ProcessPackage;
import org.eclipse.epf.uma.TeamProfile;
import org.eclipse.epf.uma.UmaFactory;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.VariabilityElement;
import org.eclipse.epf.uma.VariabilityType;
import org.eclipse.epf.uma.WorkOrder;
import org.eclipse.epf.uma.edit.command.MethodElementCreateCopyCommand;
import org.eclipse.epf.uma.edit.command.MethodElementInitializeCopyCommand;
import org.eclipse.epf.uma.provider.ActivityItemProvider;
import org.eclipse.epf.uma.util.AssociationHelper;
import org.eclipse.swt.widgets.Display;


/**
 * Abstract base class for activity's item providers used by the Process Editor.
 * 
 * @author Phong Nguyen Le
 * @since 1.0
 */
public abstract class BSActivityItemProvider extends ActivityItemProvider
		implements IProcessItemProvider, IBSItemProvider,
		ITableItemLabelProvider, IConfigurable, ICachedChildrenItemProvider {

	private Object parent;

	private int id;

	protected Object topItem = null;

	private boolean rolledUp;

	private GraphicalData graphicalData;

	private PredecessorList predecessors;

	private Boolean expanded;

	protected Adapter baseListener;

	private IFilter childFilter = new IFilter() {

		public boolean accept(Object obj) {
			return acceptAsChild(obj);
		}

	};

	private boolean refreshAllIDsRequired;

	private IFilter filter;

	protected List cachedChildren;

	protected List cachedRollupChildren;

	private IConfigurator configurator;

	private Disposable contributedWrappers;

	private VariabilityInfo variabilityInfo;
	
	private boolean enableVariabilityInfo = true;

	/**
	 * @param adapterFactory
	 */
	public BSActivityItemProvider(AdapterFactory adapterFactory) {
		super(adapterFactory);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#dispose()
	 */
	public void dispose() {
		if(baseListener != null) {
			EObject currentBase = (EObject) baseListener.getTarget();
			if(currentBase != null) {
				currentBase.eAdapters().remove(baseListener);
			}
			if (getTarget() instanceof Activity) {
				Activity act = (Activity) getTarget();
				VariabilityElement base = act.getVariabilityBasedOnElement();
				if (base != null) {
					base.eAdapters().remove(baseListener);
				}
			}
		}

		if (predecessors != null) {
			predecessors.dispose();
		}

		if(cachedChildren != null) {
			cachedChildren.clear();
			cachedChildren = null;
		}
		
		if(cachedRollupChildren != null) {
			cachedRollupChildren.clear();
			cachedRollupChildren = null;
		}
		
		if(contributedWrappers != null) {
			contributedWrappers.dispose();
			contributedWrappers = null;
		}
		
		super.dispose();
	}

	/**
	 * Checks if the given object can be accepted as a child.
	 */
	protected boolean acceptAsChild(Object child) {
		if (filter != null) {
			return filter.accept(child);
		}
		return true;
	}

	/**
	 * Checks if the given object can be accepted as a rolled-up child.
	 */
	protected boolean acceptAsRolledUpChild(Object child) {
		child = TngUtil.unwrap(child);
		if (filter != null && !(child instanceof Activity)
				&& !(child instanceof TeamProfile)) {
			return filter.accept(child);
		}
		return true;
	}

	/**
	 * Gets the object associated with the given descriptor (e.g: a
	 * TaskDescriptor is associated with a Task).
	 */
	protected abstract Object getObject(Descriptor descriptor);

	public Collection getNewChildDescriptors(Object object, EditingDomain editingDomain, Object sibling) {
		// disallow creating new child if this item provider is rolled up
		//
		if(isRolledUp()) {
			return Collections.EMPTY_LIST;
		}
		
		return super.getNewChildDescriptors(object, editingDomain, sibling);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#collectNewChildDescriptors(java.util.Collection,
	 *      java.lang.Object)
	 */
	protected void collectNewChildDescriptors(Collection newChildDescriptors,
			Object object) {
		// if(isTopActivity(object)) {
		// newChildDescriptors.add
		// (createChildParameter
		// (UmaPackage.eINSTANCE.getActivity_BreakdownElements(),
		// UmaFactory.eINSTANCE.createPhase()));
		// newChildDescriptors.add
		// (createChildParameter
		// (UmaPackage.eINSTANCE.getActivity_BreakdownElements(),
		// UmaFactory.eINSTANCE.createIteration()));
		// }
		// else if(object instanceof Phase) {
		// newChildDescriptors.add
		// (createChildParameter
		// (UmaPackage.eINSTANCE.getActivity_BreakdownElements(),
		// UmaFactory.eINSTANCE.createIteration()));
		// }
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#isExpanded()
	 */
	public Boolean isExpanded() {
		return expanded;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#setExpanded(boolean)
	 */
	public void setExpanded(Boolean b) {
		expanded = b;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.tng.process.IBSItemProvider#getId()
	 */
	public int getId() {
		return id;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.tng.process.IBSItemProvider#getTopItem()
	 */
	public Object getTopItem() {
		if (topItem == null) {
			if(ProcessUtil.isTopProcess(target)) {
				return target;
			}
			if(parent != null) {
				Object ip = getRootAdapterFactory().adapt(parent, ITreeItemContentProvider.class);
				if(ip instanceof IBSItemProvider) {
					IBSItemProvider adapter = (IBSItemProvider) ip;
					if (adapter != null) {
						Object top = adapter.getTopItem();
						if (top == null && parent instanceof Process
								&& ((Process) parent).getSuperActivities() == null) {
							top = parent;
							adapter.setTopItem(top);
						}
						return top;
					}
				}
			}
		}
		return topItem;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.tng.process.IBSItemProvider#setId(int)
	 */
	public void setId(int id) {
		this.id = id;
	}

	private static boolean isTopActivity(Object object) {
		EObject eObj = (EObject) object;
		Object parent = eObj.eContainer();
		if (parent instanceof ProcessComponent) {
			ProcessComponent pc = (ProcessComponent) parent;
			return object == pc.getProcess();
		}
		return false;
	}

	/**
	 * @param object
	 * @return
	 */
	protected static boolean hasChildDescriptor(Activity act) {
		for (Iterator iter = act.getBreakdownElements().iterator(); iter
				.hasNext();) {
			Object element = iter.next();
			if (element instanceof Descriptor) {
				return true;
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getChildrenFeatures(java.lang.Object)
	 */
	public Collection getChildrenFeatures(Object object) {
		if (childrenFeatures == null) {
			childrenFeatures = new ArrayList();
			childrenFeatures.add(UmaPackage.eINSTANCE
					.getActivity_BreakdownElements());
		}
		return childrenFeatures;
	}

	protected void setParentFor(Object child, Object parent) {
		ProcessUtil.setParent(child, parent, getRootAdapterFactory());
	}

//	private Collection getDirectChildren(Object object) {
//		List myChildren;
//		ChildrenStore store = getChildrenStore(object);
//		if (store != null) {
//			myChildren = store.getChildren();
//		} else {
//			// store = createChildrenStore(object);
//			List result = store != null ? null : new ArrayList();
//			EObject eObject = (EObject) object;
//
//			for (Iterator i = getChildrenFeatures(object).iterator(); i
//					.hasNext();) {
//				EStructuralFeature feature = (EStructuralFeature) i.next();
//				if (feature.isMany()) {					
//					List children = (List) eObject.eGet(feature);
//					
////					//Change for Activity Variability. To do realization for
////					//Browsing and Publishing.
////					List children = new ArrayList();
////					if(filter != null && filter instanceof IConfigurator){
////						children.addAll(((IConfigurator)filter).getChildren(object, feature));
////					}
////					else{
////						children = (List) eObject.eGet(feature);
////					}
//					
//					int index = 0;
//					for (Iterator ci = children.iterator(); ci.hasNext(); index++) {
//						Object child = ci.next();
//						if (acceptAsChild(child)) {
//							child = wrap(eObject, feature, child, index);
//							setParentFor(child, object);
//							// IBSItemProvider adapter = (IBSItemProvider)
//							// getBestAdapterFactory().adapt(child,
//							// ITreeItemContentProvider.class);
//							// adapter.setRolledUp(false);
//							if (store != null) {
//								store.getList(feature).add(child);
//							} else {
//								result.add(child);
//							}
//						}
//					}
//				} else {
//					Object child = eObject.eGet(feature);
//					if (acceptAsChild(child)) {
//						child = wrap(eObject, feature, child,
//								CommandParameter.NO_INDEX);
//						setParentFor(child, object);
//						// IBSItemProvider adapter = (IBSItemProvider)
//						// getBestAdapterFactory().adapt(child,
//						// ITreeItemContentProvider.class);
//						// adapter.setRolledUp(false);
//
//						if (store != null) {
//							store.setValue(feature, child);
//						} else {
//							result.add(child);
//						}
//					}
//				}
//			}
//			myChildren = store != null ? store.getChildren() : result;
//		}
//		Collection children = addInherited(object, myChildren);
//		return children;
//	}
	
	private Collection getImmediateChildren(Object object) {
		Collection children = new ArrayList();
		for (Iterator iter = super.getChildren(object).iterator(); iter.hasNext();) {
			Object child = (Object) iter.next();
			if(acceptAsChild(child)) {
				if(configurator != null) {
					child = configurator.resolve(child);
				}
				if(child != null) {
					setParentFor(child, object);
					children.add(child);
				}
			}
		}
		children = addInherited(object, (List) children);
		return children;
	}
	
	private Collection getRolledUpChildren(Object object) {
		List children = new ArrayList();
		AbstractTreeIterator iter = new AbstractTreeIterator(object, false) {

			/**
			 * Comment for <code>serialVersionUID</code>
			 */
			private static final long serialVersionUID = -3159537629619737368L;

			protected Iterator getChildren(Object object) {
				if (object instanceof Activity) {
					BSActivityItemProvider adapter = (BSActivityItemProvider) adapterFactory.adapt(object, ITreeItemContentProvider.class);
					Iterator iterator;
					boolean b = adapter.isRolledUp();
					try {
						adapter.basicSetRolledUp(false);
						iterator = adapter.getChildren(object).iterator();
					}
					finally {
						adapter.basicSetRolledUp(b);
					}
					return iterator;
				} else if (object instanceof BreakdownElementWrapperItemProvider
						&& !(object instanceof TeamProfileWrapperItemProvider)) {
					return ((BreakdownElementWrapperItemProvider) object)
							.getChildren(object).iterator();
				}
				return Collections.EMPTY_LIST.iterator();
			}

		};
		// make sure that only one descriptor for each content element in the roll-up list
		//
		List descriptors = new ArrayList();
		while (iter.hasNext()) {
			Object child = iter.next();
			if (acceptAsRolledUpChild(child)) {
				Object e = TngUtil.unwrap(child);
				if(e instanceof Descriptor && isNewDescriptor(descriptors, e)) {									
					children.add(child);
					descriptors.add(e);
				}
				else if(!(e instanceof Descriptor)){
					children.add(child);
				}
			}
		}
		descriptors.clear();
		descriptors = null;
		return children;
	}
	
	private Collection doGetChildren(Object object) {
		if(isRolledUp()) {
			return getRolledUpChildren(object);
		}
		else {
			return getImmediateChildren(object);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getChildren(java.lang.Object)
	 */
	public Collection getChildren(Object object) {
		Activity activity = (Activity) object;
		if(configurator != null) {
			
			//TODO: remove the enableVariabilityInfoFlag and  
			// create an interface or do differently
			if(enableVariabilityInfo)
				variabilityInfo = configurator.getVariabilityInfo(activity);
			else 
				variabilityInfo = null;
		}
		else {
			variabilityInfo = null;
		}
		if(variabilityInfo != null) {
			Object resolved = variabilityInfo.getInheritanceList().get(0);
			BSActivityItemProvider ip = (BSActivityItemProvider) adapterFactory.adapt(resolved, ITreeItemContentProvider.class);			
			Collection children;
			boolean b = ip.isRolledUp();
			try {
				ip.basicSetRolledUp(isRolledUp());
				children = ip.doGetChildren(resolved);
			}
			finally {
				ip.basicSetRolledUp(b);
			}
			if(!variabilityInfo.getContributors().isEmpty()) {
				ArrayList contributedChildren = new ArrayList();
				for (Iterator iter = variabilityInfo.getContributors().iterator(); iter.hasNext();) {
					Object e = iter.next();
					Object adapter = adapterFactory.adapt(e, ITreeItemContentProvider.class);
					if(adapter instanceof BSActivityItemProvider) {
						ip = ((BSActivityItemProvider)adapter);
						b = ip.isRolledUp();
						try {
							ip.basicSetRolledUp(isRolledUp());
							for (Iterator iterator = ip.getImmediateChildren(e).iterator(); iterator
							.hasNext();) {
								e = iterator.next();
								e = Providers.getConfigurationApplicator().resolve(e, configurator.getMethodConfiguration());
								if(e != null && !TngUtil.contains(children, e)) {
									contributedChildren.add(e);
								}
							}
						}
						finally {
							ip.basicSetRolledUp(b);
						}
					}
				}
				if(!contributedChildren.isEmpty()) {
					children.addAll(wrapContributed(activity, contributedChildren));
				}
			}
			return children;
		}
		return doGetChildren(object);
	}
	
	protected void updateCachedChildren(Collection children) {
//		if(isRolledUp()) {
//			if(cachedRollupChildren == null) {
//				cachedRollupChildren = new ArrayList(children);
//			}
//			else {
//				cachedRollupChildren.clear();
//				cachedRollupChildren.addAll(children);
//			}
//		}
//		else {
//			if(cachedChildren == null) {
//				cachedChildren = new ArrayList(children);
//			}
//			else {
//				cachedChildren.clear();
//				cachedChildren.addAll(children);
//			}
//		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.epf.library.edit.ICachedChildrenItemProvider#getChildrenFromCache()
	 */
	public Collection getChildrenFromCache() {
		if(cachedChildren == null) {
			getChildren(target);
		}
		return cachedChildren;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.epf.library.edit.ICachedChildrenItemProvider#getRollupChildrenFromCache()
	 */
	public Collection getRollupChildrenFromCache() {
		if(cachedRollupChildren == null) {
			boolean oldRolledUp = rolledUp;
			try {
				rolledUp = true;
				getChildren(target);
			}
			finally {
				rolledUp = oldRolledUp;
			}			
		}
		return cachedRollupChildren;
	}
	
	private BreakdownElementWrapperItemProvider findWrapper(Object owner, Object value) {
		// find if a wrapper already exist for the given value and owner
		//
		Object adapter = adapterFactory.adapt(value,
				ITreeItemContentProvider.class);
		BreakdownElementWrapperItemProvider wrapper = null;
		if (adapter instanceof IBSItemProvider) {
			List listeners = ((IBSItemProvider) adapter).getListeners();
			if (listeners != null) {
				find_wrapper_loop: for (Iterator iter = listeners.iterator(); iter
						.hasNext();) {
					Object element = (Object) iter.next();
					if (element instanceof BreakdownElementWrapperItemProvider) {
						wrapper = (BreakdownElementWrapperItemProvider) element;
						if (wrapper.getValue() == value
								&& wrapper.getParent(value) == owner) {
							break find_wrapper_loop;
						} else {
							wrapper = null;
						}
					}
				}
			}
		}
		return wrapper;
	}

	private List wrapInherited(Activity owner, Collection breakdownElements) {
		if (wrappers == null) {
			wrappers = new Disposable();
		}	
		return wrap(owner, breakdownElements, true, false, wrappers);
	}
	
	private List wrapContributed(Activity owner, Collection breakdownElements) {
		if(contributedWrappers == null) {
			contributedWrappers = new Disposable();
		}
		return wrap(owner, breakdownElements, false, true, contributedWrappers);
	}
	
	private IWrapperItemProviderFactory getWrapperItemProviderFactory() {
		IWrapperItemProviderFactory factory = null;
		if(adapterFactory instanceof IWrapperItemProviderFactoryProvider) {
			factory = ((IWrapperItemProviderFactoryProvider)adapterFactory).getWrapperItemProviderFactory();
		}
		if(factory == null) {
			factory = IBreakdownElementWrapperItemProviderFactory.INSTANCE;
		}
		return factory;
	}
	
	private List wrap(Activity owner, Collection breakdownElements, boolean inherited, boolean contributed, Disposable wrappers) {
		ArrayList wrapperList = new ArrayList();
		IWrapperItemProviderFactory wrapperFactory = getWrapperItemProviderFactory();
		for (Iterator iter = breakdownElements.iterator(); iter.hasNext();) {
			Object object = iter.next();
			BreakdownElement e = (BreakdownElement) TngUtil.unwrap(object);
			if (!TngUtil.isBase(owner.getBreakdownElements(), e)) {
				Object child = getWrapper(owner, e, wrappers);
				if(child == null) {
					child = wrapperFactory.createWrapper(e, owner, adapterFactory);
					if(child instanceof BreakdownElementWrapperItemProvider) {
						BreakdownElementWrapperItemProvider beWrapper = (BreakdownElementWrapperItemProvider) child;
						if(inherited) {
							beWrapper.isInherited = true;
						}
						else if(contributed) {
							beWrapper.contributed = true;
							if(object instanceof BreakdownElementWrapperItemProvider
									&& ((BreakdownElementWrapperItemProvider)object).isInherited()) {
								beWrapper.isInherited = true;
							}
						}
					}
				}
				else {
					wrappers.remove(child);
				}
				wrapperList.add(child);
			}
		}
		wrappers.dispose();
		wrappers.addAll(wrapperList);
		return wrapperList;
	}
	
	private static Object getWrapper(Activity owner, BreakdownElement e, Collection wrappers) {
		for (Iterator iter = wrappers.iterator(); iter.hasNext();) {
			IWrapperItemProvider wrapper = (IWrapperItemProvider) iter.next();
			if(wrapper.getOwner() == owner && wrapper.getValue() == e) {
				return wrapper;
			}
		}
		return null;
	}
	
	protected Adapter createBaseListener() {
		if (baseListener == null) {
			baseListener = new AdapterImpl() {
				/*
				 * (non-Javadoc)
				 * 
				 * @see org.eclipse.epf.uma.provider.ActivityItemProvider#notifyChanged(org.eclipse.emf.common.notify.Notification)
				 */
				public void notifyChanged(final Notification notification) {
					Activity act = (Activity) BSActivityItemProvider.this
							.getTarget();
					switch (notification.getFeatureID(Activity.class)) {
					case UmaPackage.ACTIVITY__PRESENTATION_NAME:
						if (ProcessUtil.isExtendingOrLocallyContributing(act)) {
							fireNotifyChanged(new ViewerNotification(
									notification, act, false, true));
						}
						break;

					case UmaPackage.ACTIVITY__NAME:
						if (act.getVariabilityBasedOnElement() != null) {
							fireNotifyChanged(new ViewerNotification(
									notification, act, false, true));
						}
						break;

					case UmaPackage.ACTIVITY__SUPPRESSED:
						fireNotifyChanged(new ViewerNotification(notification,
								act, true, false));
						break;
					case UmaPackage.ACTIVITY__VARIABILITY_BASED_ON_ELEMENT:
						fireNotifyChanged(new ViewerNotification(notification,
								act, true, true));
						refreshAffectedViewers();
						break;

					case UmaPackage.ACTIVITY__BREAKDOWN_ELEMENTS: {
						List list = getAffectedChildren(notification);
						if (!list.isEmpty()) {
							boolean updateLabel = refreshChildrenData(
									notification, list);

							// forward notification
							//
							fireNotifyChanged(new ViewerNotification(
									notification, act, true, updateLabel));

							// ProcessUtil.refreshViewer(getRootAdapterFactory(),
							// (Process) getTopItem());
							refreshAffectedViewers();
						}

						// try {
						// List list = getAffectedChildren(notification);
						// if(!list.isEmpty()) {
						// Display.getCurrent().asyncExec(new Runnable() {
						//										
						// public void run() {
						// AdapterFactory rootAdapterFactory =
						// getRootAdapterFactory();
						// if(ProcessUtil.refreshNeeded(rootAdapterFactory,
						// BSActivityItemProvider.this)) {
						// Process topAct = (Process) getTopItem();
						//												
						// // recalculate the IDs of breakdown elements and
						// refresh them
						// //
						// // TngUtil.updateIDs(rootAdapterFactory, new
						// AdapterFactoryTreeIterator(rootAdapterFactory,
						// topAct), topAct);
						// ProcessUtil.updateIDs(rootAdapterFactory, topAct);
						//												
						// // refresh children
						// //
						// fireNotifyChanged(new
						// ViewerNotification(notification,
						// BSActivityItemProvider.this.getTarget(), true,
						// false));
						//												
						// ProcessUtil.refreshIDsInViewers((ExposedAdapterFactory)
						// rootAdapterFactory);
						// }
						// }
						//										
						// });
						// }
						// }
						// catch(Exception e) {
						// TngEditPlugin.INSTANCE.log(e);
						// }

						break;
					}
					}
				}
			};
		}

		return baseListener;
	}

	protected Activity listenToBaseActivity() {
		Activity act = (Activity) target;
		if (!ProcessUtil.isExtendingOrLocallyContributing(act)) {
			return null;
		}
		Activity baseAct;
		if(variabilityInfo != null) {
			if(variabilityInfo.getInheritanceList().size() > 1) {
				baseAct = (Activity) variabilityInfo.getInheritanceList().get(1);
			}			
			else {
				baseAct = (Activity) configurator.resolve(act.getVariabilityBasedOnElement());
			}
		}
		else {
			baseAct = (Activity) act.getVariabilityBasedOnElement();
		}
		if (baseAct == null) {
			return null;
		}
		
		createBaseListener();
		
		// remove baseListener from old base, if there is any
		//
		EObject oldBase = (EObject) baseListener.getTarget();
		if(oldBase != null) {
			oldBase.eAdapters().remove(baseListener);
		}
		
		if (!baseAct.eAdapters().contains(baseListener)) {
			baseAct.eAdapters().add(baseListener);
		}

		Object ip = adapterFactory.adapt(baseAct, ITreeItemContentProvider.class);
		if(ip instanceof BSActivityItemProvider) {
			BSActivityItemProvider adapter = (BSActivityItemProvider) ip;
			adapter.listenToBaseActivity();
		}
		else {
			System.out.println();
		}
		return baseAct;
	}

	protected Collection addInherited(Object object, List myChildren) {
		if (object instanceof Activity) {
			Activity act = (Activity) object;
			Activity baseAct = listenToBaseActivity();
			if (baseAct != null) {
				VariabilityType extendType = act.getVariabilityType();
				if (extendType == VariabilityType.LOCAL_REPLACEMENT_LITERAL) {
					return myChildren;
				} else if (extendType == VariabilityType.LOCAL_CONTRIBUTION_LITERAL
						|| extendType == VariabilityType.EXTENDS_LITERAL) {
					BSActivityItemProvider adapter = (BSActivityItemProvider) getRootAdapterFactory()
						.adapt(baseAct, ITreeItemContentProvider.class);
					List allChildren;					
					boolean oldRolledUp = adapter.isRolledUp();
					try {
						if(oldRolledUp) {
							adapter.basicSetRolledUp(false);
						}
						allChildren = wrapInherited(act, adapter.getChildren(baseAct));
					}
					finally {
						if(oldRolledUp) {
							adapter.basicSetRolledUp(true);
						}
					}
//					// go thru own children list and insert it to the combined
//					// child list at the right place.
//					//
//					for (Iterator iter = myChildren.iterator(); iter.hasNext();) {
//						BreakdownElement element = (BreakdownElement) iter
//								.next();
//						TngUtil.addTo(allChildren, element, object, TngUtil
//								.getBestAdapterFactory(adapterFactory));
//					}
					if(allChildren.isEmpty()) {
						return myChildren;
					}
					else {
						TngUtil.addAllTo(allChildren, myChildren);
						return allChildren;
					}
				}
			}
		}
		return myChildren;
	}

	/**
	 * Checks if there is no descriptor in <code>children</code> referring to
	 * the same object as the specified descriptor <code>child</code> does.
	 * 
	 * @param children
	 * @param child
	 * @return true if no descriptor is found in the children list that refers
	 *         to the same object as the specified child does, false otherwise.
	 */
	protected boolean isNewDescriptor(List children, Object child) {
		Object obj = getObject((Descriptor) TngUtil.unwrap(child));
		if (obj == null)
			return true;
		for (int i = children.size() - 1; i > -1; i--) {
			if (obj == getObject((Descriptor) TngUtil.unwrap(children.get(i))))
				return false;
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.command.CreateChildCommand.Helper#getCreateChildImage(java.lang.Object,
	 *      java.lang.Object, java.lang.Object, java.util.Collection)
	 */
	public Object getCreateChildImage(Object owner, Object feature,
			Object child, Collection selection) {
		// return
		// TngEditPlugin.INSTANCE.getImage("full/ctool16/CreateActivity_breakdownElements_Activity");
		return getImage(child);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#createRemoveCommand(org.eclipse.emf.edit.domain.EditingDomain,
	 *      org.eclipse.emf.ecore.EObject,
	 *      org.eclipse.emf.ecore.EStructuralFeature, java.util.Collection)
	 */
	protected Command createRemoveCommand(EditingDomain domain, EObject owner,
			EStructuralFeature feature, Collection collection) {
		return super.createRemoveCommand(domain, owner, feature, collection);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.tng.process.IBSItemProvider#getItemProvider(org.eclipse.emf.ecore.EObject)
	 */
	public IBSItemProvider getItemProvider(EObject eObj) {
		return (IBSItemProvider) TngUtil
				.getAdapter(eObj, IBSItemProvider.class);
	}

	public void setParent(Object object) {
		parent = object;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getParent(java.lang.Object)
	 */
	public Object getParent(Object object) {
		return parent;
	}

	/**
	 * Gets the list of children that had been affected after a an ADD, REMOVE,
	 * or MOVE operation.
	 */
	protected List getAffectedChildren(Notification notification) {
		return ProcessUtil.getAffectedElements(notification, childFilter);
	}

	protected boolean refreshChildrenData(Notification notification,
			List affectedChildren) {
		return false;
	}

	protected void doRefreshChildren(Notification notification,
			List affectedChildren) {
		refreshChildrenData(notification, affectedChildren);

		Process topAct = (Process) getTopItem();
		AdapterFactory rootAdapterFactory = getRootAdapterFactory();
		ProcessUtil.refreshViewer(rootAdapterFactory, topAct);
	}

	protected void refreshChildren(final Notification notification,
			final List newOrOldChildren) {
		if (!newOrOldChildren.isEmpty()) {

			Display display = null;
			try {
				display = Display.getCurrent();
			} catch (Exception e) {
				//
			}
			Runnable runnable = new Runnable() {

				public void run() {
					doRefreshChildren(notification, newOrOldChildren);
				}

			};
			if (display != null) {
				display.asyncExec(runnable);
			} else {
				runnable.run();
			}
		}
	}

	protected void refreshAffectedViewers() {
		Display display = null;
		try {
			display = Display.getCurrent();
		} catch (Exception e) {
			//
		}
		Runnable runnable = new Runnable() {

			public void run() {
				doRefreshAffectedViewers();
			}

		};
		if (display != null) {
			display.asyncExec(runnable);
		} else {
			runnable.run();
		}
	}

	protected void doRefreshAffectedViewers() {

	}
	
	protected boolean handlePredecessorListChange(Notification notification) {
		return TngUtil.handlePredecessorListChange(this, notification);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.common.notify.Adapter#notifyChanged(org.eclipse.emf.common.notify.Notification)
	 */
	public void notifyChanged(Notification notification) {
		updateChildren(notification);

		if (handlePredecessorListChange(notification))
			return;

		switch (notification.getFeatureID(Activity.class)) {
		case UmaPackage.ACTIVITY__BREAKDOWN_ELEMENTS:
			List newOrOldChildren = getAffectedChildren(notification);
			if (!newOrOldChildren.isEmpty()) {
				switch (notification.getEventType()) {
				case Notification.ADD:
				case Notification.ADD_MANY:
					createOrMovePackageFor(newOrOldChildren);
					break;
				}

				fireNotifyChanged(new ViewerNotification(notification,
						notification.getNotifier(), true, false));

				// refreshChildren(notification, newOrOldChildren);

				refreshAffectedViewers();
			}
			return;
		case UmaPackage.ACTIVITY__SUPPRESSED:
			fireNotifyChanged(new ViewerNotification(notification, notification
					.getNotifier(), true, true));
			return;
		case UmaPackage.ACTIVITY__VARIABILITY_TYPE:
		case UmaPackage.ACTIVITY__VARIABILITY_BASED_ON_ELEMENT:

			fireNotifyChanged(new ViewerNotification(notification, notification
					.getNotifier(), true, true));

			// ProcessUtil.refreshViewer(getRootAdapterFactory(), (Process)
			// getTopItem());

			refreshAffectedViewers();

			return;
		}

		super.notifyChanged(notification);
	}

	/**
	 * @param newOrOldChildren
	 */
	protected void createOrMovePackageFor(List newChildren) {
		ProcessPackage pkg = (ProcessPackage) ((EObject) target).eContainer();
		for (Iterator iter = newChildren.iterator(); iter.hasNext();) {
			Object element = iter.next();
			if (element instanceof Activity) {
				Activity act = (Activity) element;
				if (act.eContainer() == null) {
					// create new ProcessPackage for the activity
					//
					ProcessPackage newPkg = UmaFactory.eINSTANCE
							.createProcessPackage();
					newPkg.setName(act.getName());
					pkg.getChildPackages().add(newPkg);
					newPkg.getProcessElements().add(element);
				} else {
					// Check if the activity's ProcessPackage is at the right
					// place.
					// If not, move it to the right ProcessComponent.
					//
					ProcessPackage oldPkg = (ProcessPackage) act.eContainer();
					if (oldPkg.eContainer() != pkg) {
						pkg.getChildPackages().add(oldPkg);
					}
				}
			} else {
				pkg.getProcessElements().add(element);
			}
		}
	}

	private static void getAllBreakdownElements(Set set, BreakdownElement e) {
		boolean ret = set.add(e);
		if (ret && (e instanceof Activity)) {
			for (Iterator iter = ((Activity) e).getBreakdownElements()
					.iterator(); iter.hasNext();) {
				BreakdownElement element = (BreakdownElement) iter.next();
				getAllBreakdownElements(set, element);
			}
		}
	}

	/**
	 * Refresh the comma-separated list of predecessor IDs in the viewer
	 * 
	 * @param affectedBreakdownElements
	 */
	private void refreshPredecessors(Notification msg,
			List affectedBreakdownElements) {

		Set refreshList = new HashSet();
		Set allBreakdownElements = new HashSet();
		for (Iterator iter = affectedBreakdownElements.iterator(); iter
				.hasNext();) {
			BreakdownElement e = (BreakdownElement) iter.next();
			if (msg.getEventType() == Notification.REMOVE
					|| msg.getEventType() == Notification.REMOVE_MANY) {
				setParentFor(e, null);
			}
			getAllBreakdownElements(allBreakdownElements, e);
		}
		// System.out.println("BSActivityItemProvider.refreshPredecessors():
		// allBreakdownElements="+allBreakdownElements);
		for (Iterator iterator = allBreakdownElements.iterator(); iterator
				.hasNext();) {
			BreakdownElement element = (BreakdownElement) iterator.next();
			for (Iterator iterator1 = AssociationHelper.getLinkToSuccessor(
					element).iterator(); iterator1.hasNext();) {
				WorkOrder workOrder = (WorkOrder) iterator1.next();
				BreakdownElement succ = AssociationHelper
						.getSuccessor(workOrder);
				if (!allBreakdownElements.contains(succ)) {
					refreshList.add(succ);
				}
			}
		}

		// System.out.println("BSActivityItemProvider.refreshPredecessors():
		// refreshList=" + refreshList);

		for (Iterator iter = refreshList.iterator(); iter.hasNext();) {
			Object child = iter.next();
			fireNotifyChanged(new ViewerNotification(msg, child, false, true));
		}
	}

	/**
	 * @param activity
	 */
	private void createPackageFor(Activity activity, EObject parentActivity) {
		ProcessPackage parent = (ProcessPackage) parentActivity.eContainer();
		ProcessPackage newPkg = UmaFactory.eINSTANCE.createProcessPackage();
		newPkg.setName(activity.getName());
		parent.getChildPackages().add(newPkg);
		ArrayList breakdownElements = new ArrayList();
		breakdownElements.add(activity);
		breakdownElements.addAll(activity.getBreakdownElements());
		newPkg.getProcessElements().addAll(breakdownElements);
	}

	protected String getColumnName(int columnIndex) {
		AdapterFactory rootAdapterFactory = getRootAdapterFactory();
		if (rootAdapterFactory instanceof IColumnAware) {
			Map columnIndexToNameMap = ((IColumnAware) rootAdapterFactory)
					.getColumnIndexToNameMap();
			if (columnIndexToNameMap != null) {
				return (String) columnIndexToNameMap.get(new Integer(
						columnIndex));
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ITableItemLabelProvider#getColumnImage(java.lang.Object,
	 *      int)
	 */
	public Object getColumnImage(Object object, int columnIndex) {
		String colName = getColumnName(columnIndex);
		return TngUtil.getColumnImage(object, colName);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ITableItemLabelProvider#getColumnText(java.lang.Object,
	 *      int)
	 */
	public String getColumnText(Object object, int columnIndex) {
		String colName = getColumnName(columnIndex);
		return getAttribute(object, colName);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getText(java.lang.Object)
	 */
	public String getText(Object object) {
		return TngUtil.getLabel(object);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.tng.process.IBSItemProvider#setTopItem(java.lang.Object)
	 */
	public void setTopItem(Object top) {
		topItem = top;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#isRolledUp()
	 */
	public boolean isRolledUp() {
		return rolledUp;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#setRolledUp(boolean)
	 */
	public void setRolledUp(boolean b) {
		rolledUp = b;
		if (!rolledUp) {
			// user roll down this activity, set rolledUp flag of all its
			// offstring to false
			//
			Iterator iter = new AbstractTreeIterator(target, false) {

				/**
				 * Comment for <code>serialVersionUID</code>
				 */
				private static final long serialVersionUID = -7840687264753640250L;

				protected Iterator getChildren(Object object) {
					ITreeItemContentProvider adapter = (ITreeItemContentProvider) adapterFactory
							.adapt(object, ITreeItemContentProvider.class);
					return adapter.getChildren(object).iterator();
				}

			};
			while (iter.hasNext()) {
				Object child = (Object) iter.next();
				Object adapter = adapterFactory.adapt(child,
						ITreeItemContentProvider.class);
				if (adapter instanceof IBSItemProvider) {
					((IBSItemProvider) adapter).setRolledUp(false);
				}
			}
		}
	}

	public GraphicalData getGraphicalData() {
		return graphicalData;
	}

	public void setGraphicalData(GraphicalData data) {
		graphicalData = data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#getAttribute(java.lang.Object,
	 *      java.lang.String)
	 */
	public String getAttribute(Object object, String property) {
		return ProcessUtil.getAttribute(object, property, this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#setAttribute(java.lang.Object,
	 *      java.lang.String, java.lang.String)
	 */
	public void setAttribute(Object object, String prop, String txt) {
		Activity e = (Activity) object;
		ProcessUtil.setAttribute(e, prop, txt);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#createCreateChildCommand(org.eclipse.emf.edit.domain.EditingDomain,
	 *      org.eclipse.emf.ecore.EObject,
	 *      org.eclipse.emf.ecore.EStructuralFeature, java.lang.Object, int,
	 *      java.util.Collection)
	 */
	protected Command createCreateChildCommand(EditingDomain domain,
			EObject owner, EStructuralFeature feature, Object value, int index,
			Collection collection) {
		if (value instanceof Iteration) {
			((Iteration) value).setIsRepeatable(Boolean.TRUE);
		}
		return super.createCreateChildCommand(domain, owner, feature, value,
				index, collection);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#getListeners()
	 */
	public List getListeners() {
		if (changeNotifier == null)
			return null;
		return Collections.unmodifiableList((List) changeNotifier);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#getPredecessors()
	 */
	public PredecessorList getPredecessors() {
		if (predecessors == null) {
			predecessors = new PredecessorList(TngUtil
					.getBestAdapterFactory(adapterFactory), target);
		}
		return predecessors;
	}

	public boolean isFirstElement(Object obj) {
		return ProcessUtil.isFirstElement(getRootAdapterFactory(), this, obj);
	}

	public boolean isLastElement(Object obj) {
		return ProcessUtil.isLastElement(getRootAdapterFactory(), this, obj);
	}

	public void moveUp(Object obj, IActionManager actionMgr) {
		Object parent = this.getParent(obj);
		if ((parent != null) && (parent instanceof Activity)) {
			TngUtil.moveUp((Activity) parent, obj, getEClasses(), actionMgr);
		}
	}

	public void moveDown(Object obj, IActionManager actionMgr) {
		Object parent = this.getParent(obj);
		if ((parent != null) && (parent instanceof Activity)) {
			TngUtil.moveDown((Activity) parent, obj, getEClasses(), actionMgr);
		}
	}

	protected Command createInitializeCopyCommand(EditingDomain domain,
			EObject owner, Helper helper) {
		return new MethodElementInitializeCopyCommand(domain, owner, helper);
	}

	protected Command createCreateCopyCommand(EditingDomain domain,
			EObject owner, Helper helper) {
		return new MethodElementCreateCopyCommand(domain, owner, helper);
	}

	protected Command createAddCommand(EditingDomain domain, EObject owner,
			EStructuralFeature feature, Collection collection, int index) {
		return new ActivityAddCommand((AddCommand) super.createAddCommand(
				domain, owner, feature, collection, index));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.provider.ActivityItemProvider#getImage(java.lang.Object)
	 */
	public Object getImage(Object object) {
		Object img = TngUtil.getImage(object);
		return img != null ? img : super.getImage(object);
	}

	public IFilter getChildFilter() {
		return childFilter;
	}

	public void setRefreshAllIDsRequired(boolean b) {
		refreshAllIDsRequired = b;
	}

	public boolean isRefreshAllIDsRequired() {
		return refreshAllIDsRequired;
	}

	public IFilter getFilter() {
		return this.filter;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.IConfigurable#setFilter(com.ibm.library.edit.IFilter)
	 */
	public void setFilter(IFilter filter) {
		this.filter = filter;
		if(filter instanceof IConfigurator) {
			configurator = (IConfigurator)filter;
		}
		else {
			configurator = null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.IConfigurable#setLabel(java.lang.String)
	 */
	public void setLabel(String label) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#createDropCommand(java.lang.Object,
	 *      java.util.List)
	 */
	public IResourceAwareCommand createDropCommand(Object owner,
			List dropElements) {
		return null;
	}
	
	public void basicSetRolledUp(boolean b) {
		rolledUp = b;
	}
	
	/**
	 * @return the enableVariabilityInfo
	 */
	public boolean isEnableVariabilityInfo() {
		return enableVariabilityInfo;
	}

	/**
	 * @param enableVariabilityInfo the enableVariabilityInfo to set
	 */
	public void setEnableVariabilityInfo(boolean enableVariabilityInfo) {
		this.enableVariabilityInfo = enableVariabilityInfo;
	}
}