//------------------------------------------------------------------------------
// 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.command;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.command.AbstractCommand;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.epf.library.edit.IConfigurationApplicator;
import org.eclipse.epf.library.edit.LibraryEditPlugin;
import org.eclipse.epf.library.edit.Providers;
import org.eclipse.epf.library.edit.command.BatchCommand;
import org.eclipse.epf.library.edit.command.INestedCommandProvider;
import org.eclipse.epf.library.edit.command.IResourceAwareCommand;
import org.eclipse.epf.library.edit.ui.UserInteractionHelper;
import org.eclipse.epf.library.edit.util.ExtensionManager;
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.Descriptor;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodPackage;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.Role;
import org.eclipse.epf.uma.RoleDescriptor;
import org.eclipse.epf.uma.Task;
import org.eclipse.epf.uma.TaskDescriptor;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.WorkProductDescriptor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Shell;


/**
 * Abstract base DropCommand class for breakdown structure editor
 * 
 * @author Phong Nguyen Le
 * @since 1.0
 */
public abstract class BSDropCommand extends AbstractCommand implements
		IResourceAwareCommand {
	public static final Set DEFAULT_SYNCH_FEATURES = Collections.unmodifiableSet(new HashSet(Arrays.asList(
			new EStructuralFeature[] {
					UmaPackage.eINSTANCE.getNamedElement_Name(),
					UmaPackage.eINSTANCE.getMethodElement_BriefDescription(),
					UmaPackage.eINSTANCE.getDescribableElement_PresentationName(),
					UmaPackage.eINSTANCE.getTask_OptionalInput(),
					UmaPackage.eINSTANCE.getTask_MandatoryInput(),
					UmaPackage.eINSTANCE.getTask_Output(),
					UmaPackage.eINSTANCE.getTask_Steps(),				
					UmaPackage.eINSTANCE.getTask_PerformedBy(),
					UmaPackage.eINSTANCE.getTask_AdditionallyPerformedBy(),
					UmaPackage.eINSTANCE.getRole_ResponsibleFor(),
					UmaPackage.eINSTANCE.getArtifact_ContainedArtifacts(),
					UmaPackage.eINSTANCE.getDeliverable_DeliveredWorkProducts()
			}
	)));
	
	/**
	 * Map of linked element's feature to descriptor's feature
	 */
	public static final Map FEATURE_MAP = new HashMap();
	static {
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getNamedElement_Name(), UmaPackage.eINSTANCE.getNamedElement_Name());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getMethodElement_BriefDescription(), UmaPackage.eINSTANCE.getMethodElement_BriefDescription());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getDescribableElement_PresentationName(), UmaPackage.eINSTANCE.getDescribableElement_PresentationName());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getTask_OptionalInput(), UmaPackage.eINSTANCE.getTaskDescriptor_OptionalInput());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getTask_MandatoryInput(), UmaPackage.eINSTANCE.getTaskDescriptor_MandatoryInput());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getTask_Output(), UmaPackage.eINSTANCE.getTaskDescriptor_Output());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getRole_ResponsibleFor(), UmaPackage.eINSTANCE.getRoleDescriptor_ResponsibleFor());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getDeliverable_DeliveredWorkProducts(), UmaPackage.eINSTANCE.getWorkProductDescriptor_DeliverableParts());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getTask_Steps(), UmaPackage.eINSTANCE.getTaskDescriptor_SelectedSteps());
//		FEATURE_MAP.put(UmaPackage.eINSTANCE.getEstimatedMethodElement_Estimate(), UmaPackage.eINSTANCE.getEstimatedMethodElement_Estimate());
		FEATURE_MAP.put(UmaPackage.eINSTANCE.getTask_AdditionallyPerformedBy(), UmaPackage.eINSTANCE.getTaskDescriptor_AdditionallyPerformedBy());
	}

	protected Activity activity;

	protected List dropElements;

	private Set modifiedResources;

	protected List elementsToAddToDefaultConfig;

	protected Set addedObjects;

	private Process process;

	private boolean addedToDefaultConfig = false;

	protected boolean synchronize = false;

	protected ArrayList taskDescList;

	protected ArrayList taskDescriptorsToUpdate;

//	protected Set descriptorsToRefresh;

	/**
	 * Map of descriptor to old refreshable feature map
	 */
	private Map descriptorToOldRefreshableFeaturesMap;

	/**
	 * Map of descriptor to map of feature to new values
	 */
//	protected Map descriptorToNewFeatureValuesMap;
//	private HashMap descriptorToOldFeatureValuesMap;	
	protected DescriptorUpdateBatchCommand batchCommand;

//	private HashMap wpdToOldResponsibleRoleMap;

	/**
	 * Map of TaskDescriptor to list of newly added steps
	 */
	private Map taskDescToNewStepsMap;

	private Map taskDescToOldEstimateMap;
	
	protected boolean canceled;

	private MethodConfiguration config;

	protected Set synchFeatures = DEFAULT_SYNCH_FEATURES;

	private BatchCommand refreshResponsibleForCommand;

	protected boolean executed;

//	protected Object UIContext;
	
	protected boolean runAsJob;
	
	private IProgressMonitor monitor;

	private int workedUnits;

	private ArrayList nestedCommands;

	/**
	 * @param activity
	 *            the activity that the given elements are dropped on
	 * @param dropElements
	 *            the elements to drop on the given activity
	 */
	public BSDropCommand(Activity activity, List dropElements) {
		this(activity, dropElements, null, null, false);
	}

	/**
	 * @param synch
	 *            if true, the command will clean the content and relationships
	 *            of existing descriptors and refresh them with the linked
	 *            MethodElement
	 */
	public BSDropCommand(Activity activity, List dropElements, boolean synch) {
		this(activity, dropElements, null, null, synch);
	}

	/**
	 * Synchronize the given content elements to its TaskDescriptors in the
	 * given activity using the given MethodConfiguration and list of
	 * synchronizable features.
	 * 
	 * @param activity
	 * @param dropElements
	 *            the elements to synchronize
	 * @param config
	 * @param synchFeatures
	 *            the synchronizable features, if <code>null</code> the DEFAULT_SYNCH_FEATURES will be used.
	 */
	public BSDropCommand(Activity activity, List dropElements,
			MethodConfiguration config, Set synchFeatures) {
		this(activity, dropElements, config, synchFeatures, true);
	}
	
	public BSDropCommand(Activity activity, List dropElements,
			MethodConfiguration config, Set synchFeatures, boolean synch) {
		super();
		this.activity = activity;
		process = TngUtil.getOwningProcess(activity);
		this.synchronize = synch;
		this.config = config;
		if (synchFeatures == null) {
			this.synchFeatures = DEFAULT_SYNCH_FEATURES;
		}
		else {
			this.synchFeatures = synchFeatures;
		}
		
		this.dropElements = dropElements;
	}
	
	protected IProgressMonitor getProgressMonitor() {
		if(monitor == null) {
			monitor = new NullProgressMonitor();
		}
		return monitor;
	}
	
	protected void prepareDropElements() {
		
		ArrayList elements = new ArrayList();
		getMethodConfiguration();
		for (Iterator iter = dropElements.iterator(); iter.hasNext();) {
			Object element = TngUtil.unwrap(iter.next());
			element = Providers.getConfigurationApplicator().resolve(element, config);
			if (!elements.contains(element)
					&& !(element instanceof EObject && ((EObject) element)
							.eIsProxy())) {				
				elements.add(element);
			}
		}
		dropElements = elements;
	}
	
//	/**
//	 * Sets UI context for this command, this can be a Shell object
//	 * @param UIContext
//	 */
//	public void setUIContext(Object UIContext) {
//		this.UIContext = UIContext;
//	}

	public MethodConfiguration getMethodConfiguration() {
		if(config == null) {
			config = TngUtil.getOwningProcess(activity).getDefaultContext();
		}
		return config;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.common.command.AbstractCommand#dispose()
	 */
	public void dispose() {
		if (dropElements != null) {
			dropElements.clear();
		}
		if (modifiedResources != null) {
			modifiedResources.clear();
		}
		if (elementsToAddToDefaultConfig != null) {
			elementsToAddToDefaultConfig.clear();
		}
		if (addedObjects != null) {
			addedObjects.clear();
		}
		if (descriptorToOldRefreshableFeaturesMap != null) {
			descriptorToOldRefreshableFeaturesMap.clear();
		}
		if (taskDescList != null) {
			taskDescList.clear();
		}
		if (taskDescToNewStepsMap != null) {
			taskDescToNewStepsMap.clear();
		}
		if (taskDescToOldEstimateMap != null) {
			taskDescToOldEstimateMap.clear();
		}
		if (refreshResponsibleForCommand != null) {
			refreshResponsibleForCommand.dispose();
		}
		if (taskDescriptorsToUpdate != null) {
			taskDescriptorsToUpdate.clear();
		}
		if(batchCommand != null) {
			batchCommand.dispose();
		}
		activity = process = null;
		super.dispose();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.command.IResourceAwareCommand#getModifiedResources()
	 */
	public Collection getModifiedResources() {
		if (modifiedResources == null) {
			modifiedResources = new HashSet();
			if (activity.eResource() != null) {
				modifiedResources.add(activity.eResource());
			}
		}
		
		if(nestedCommands != null && !nestedCommands.isEmpty()) {
			for(int i = nestedCommands.size() - 1; i > -1; i--) {
				Command cmd = (Command) nestedCommands.get(i);
				if(cmd instanceof IResourceAwareCommand) {
					try {
						Collection resources = ((IResourceAwareCommand)cmd).getModifiedResources();
						if(resources != null) {
							modifiedResources.addAll(resources);
						}
					}
					catch(Exception e) {
						LibraryEditPlugin.getDefault().getLogger().logError(e);
					}
				}
			}

		}

		return modifiedResources;
	}

	/**
	 * Clears all the refreshable features of the given descriptor
	 * 
	 * @param descriptor
	 */
	private boolean clearDescriptor(Descriptor descriptor) {
		Set excludeSynchFeatures = new HashSet(DEFAULT_SYNCH_FEATURES);
		excludeSynchFeatures.removeAll(synchFeatures);
		Set excludeFeatures;
		if (excludeSynchFeatures.isEmpty()) {
			excludeFeatures = excludeSynchFeatures;
		}
		else {
			excludeFeatures = new HashSet();
			for (Iterator iter = excludeSynchFeatures.iterator(); iter.hasNext();) {
				EStructuralFeature feature = getDescriptorFeature((EStructuralFeature) iter.next());
				if(feature != null) {
					excludeFeatures.add(feature);
				}
			}
		}
		boolean ret = ProcessCommandUtil.clearDescriptor(descriptor,
				descriptorToOldRefreshableFeaturesMap, excludeFeatures);
		if (ret && descriptor instanceof TaskDescriptor) {
			if (taskDescriptorsToUpdate == null) {
				taskDescriptorsToUpdate = new ArrayList();
			}
			taskDescriptorsToUpdate.add(descriptor);
		}
		return ret;
	}

	/**
	 * @param feature
	 * @return
	 */
	private static EStructuralFeature getDescriptorFeature(EStructuralFeature linkedElementFeature) {
		return (EStructuralFeature) FEATURE_MAP.get(linkedElementFeature);
	}
	
	protected boolean preExecute() {
		prepareDropElements();
		
		boolean b = !dropElements.isEmpty();
		if (b) {
//			descriptorToNewFeatureValuesMap = new HashMap();
			batchCommand = new DescriptorUpdateBatchCommand(false, synchFeatures, config); //TODO: why clear must be false ???

//			if (synchronize) {
//				descriptorsToRefresh = new HashSet();
//			}
		}
		return b;
	}
	
	protected boolean collectElementsToAddToDefaultConfig() {
		if (elementsToAddToDefaultConfig == null) {
			elementsToAddToDefaultConfig = new ArrayList();
			for (Iterator iter = dropElements.iterator(); iter.hasNext();) {
				Object element = iter.next();
				switch (UserInteractionHelper.checkAgainstDefaultConfiguration(
						process, element)) {
				case 0:
					iter.remove();
					break;
				case 2:
					elementsToAddToDefaultConfig.add(element);
					break;
				case -1:
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Adds elements to the default configuration
	 * 
	 * @return true if default configuration has been modified, false otherwise
	 */
	private boolean addToDefaultConfiguration() {
		if (!addedToDefaultConfig && elementsToAddToDefaultConfig != null && !elementsToAddToDefaultConfig.isEmpty()) {
			if(addedObjects == null) {
				addedObjects = new HashSet();
			}
			else {
				addedObjects.clear();
			}
			for (Iterator iter = elementsToAddToDefaultConfig.iterator(); iter
			.hasNext();) {
				EObject element = (EObject) iter.next();
				ProcessUtil.addToDefaultConfiguration(process, element,
						addedObjects);
			}
			if (!addedObjects.isEmpty()) {
				getModifiedResources().add(
						process.getDefaultContext().eResource());
				return true;
			}
			addedToDefaultConfig = true;
		}
		return false;
	}
	
	protected int getTotalWork() {
		return 20;
	}
	
	protected void worked(int unitsOfWork) {
		monitor.worked(unitsOfWork);
		workedUnits = workedUnits + unitsOfWork;
	}
	
	protected int getRemainingWork() {
		int remaining = getTotalWork() - workedUnits;
		if(remaining < 0) {
			remaining = 0;
		}
		return remaining;
	}

	protected boolean updateDefaultConfigurationNeeded() {
		return !synchronize;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.common.command.Command#execute()
	 */
	public void execute() {		
		// BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
		//			
		// public void run() {
		// canceled = !addToDefaultConfiguration();
		// }
		//			
		// });
		//		
		// //TODO: This caused NullPointerException or ThreadAccessError when
		// showing selection dialog is needed
		// //
		// IRunnableWithProgress runnable = new IRunnableWithProgress() {
		//
		// public void run(IProgressMonitor monitor) throws
		// InvocationTargetException, InterruptedException {
		// canceled = !preExecute();
		// }
		//			
		// };
		// UserInteractionHelper.runWithProgress(runnable, null);
		//		
		// if(!canceled) {
		// BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
		//				
		// public void run() {
		// redo();
		// }
		//				
		// });
		// }

//		BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
//
//			public void run() {
//				if ((!synchronize && addToDefaultConfiguration()) && preExecute()) {
//					redo();
//				}
//			}
//
//		});
		
		if(updateDefaultConfigurationNeeded()) {
			if(!collectElementsToAddToDefaultConfig()) {
				return;
			}
		}
		
		final String taskName = (label == null ? "" : label); //$NON-NLS-1$
		IRunnableWithProgress runnable = new IRunnableWithProgress() {

			public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
				try {
					monitor.beginTask(taskName, getTotalWork());
					BSDropCommand.this.monitor = monitor;
					worked(getRemainingWork() / 3);
					
					// this must be called before preExecute() to add required elements to configuration
					// so the command can realize data correctly
					//
					addToDefaultConfiguration();
					worked(getRemainingWork() / 3);
					
					if (preExecute()) {
						worked(getRemainingWork() / 3);
						redo();
					}
				}
				finally {
					monitor.done();
				}
			}
			
		};
		if(runAsJob) {
			UserInteractionHelper.runAsJob(runnable, taskName);
		}
		else {
			UserInteractionHelper.runInUI(runnable, (Shell)null);
		}
	}
	
	protected List getNestedCommands() {
		if(nestedCommands == null) {
			nestedCommands = new ArrayList();
		}
		return nestedCommands;
	}
	
	protected void executeNestedCommands() {
		List nestedCommandProviders = ExtensionManager.getNestedCommandProviders();
		if(!nestedCommandProviders.isEmpty()) {
			for (Iterator iter = nestedCommandProviders.iterator(); iter
			.hasNext();) {
				INestedCommandProvider cmdProvider = (INestedCommandProvider) iter.next();
				try {
					Command cmd = cmdProvider.createNestedCommand(this);
					if(cmd != null && cmd.canExecute()) {							
						cmd.execute();
						getNestedCommands().add(cmd);
					}
				}
				catch(Exception e) {
					LibraryEditPlugin.getDefault().getLogger().logError(e);
				}
			}
		}	
	}
	
	protected void undoNestedCommands() {
		if(nestedCommands != null && !nestedCommands.isEmpty()) {
			for(int i = nestedCommands.size() - 1; i > -1; i--) {
				Command cmd = (Command) nestedCommands.get(i);
				try {
					if(cmd.canUndo()) {
						cmd.undo();
					}
				}
				catch(Exception e) {
					LibraryEditPlugin.getDefault().getLogger().logError(e);
				}
				finally {
					try { cmd.dispose(); } catch(Exception e) {}
				}
			}
			nestedCommands.clear();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.common.command.Command#redo()
	 */
	public void redo() {
		try {
			// add elements to the default configuration if needed
			//
			addToDefaultConfiguration();
			
			doExecute();
			updateDescriptors();
			
			executeNestedCommands();
			
			executed = true;
		}
		catch(RuntimeException e) {
			e.printStackTrace();
			LibraryEditPlugin.getDefault().getLogger().logError(e);
			throw e;
		}
	}

	/**
	 * Updates descriptors
	 */
	protected void updateDescriptors() {
		if (synchronize) {
			clearDescriptors();
		}

		// update new values;
		//
		batchCommand.execute();
		
		updateTaskDescriptors();

		setResponsibleRoles();
	}

//	/**
//	 * @param desc
//	 * @param feature
//	 * @param value
//	 */
//	protected void saveOldFeatureValue(Descriptor desc,
//			EStructuralFeature feature) {
//		if (descriptorToOldFeatureValuesMap == null) {
//			descriptorToOldFeatureValuesMap = new HashMap();
//		}
//		Object value = desc.eGet(feature);
//		if(feature.isMany()) {
//			value = new ArrayList((List)value);
//		}
//		Map featureMap = (Map) descriptorToOldFeatureValuesMap.get(desc);
//		if (featureMap == null) {
//			featureMap = new HashMap();
//			descriptorToOldFeatureValuesMap.put(desc, featureMap);
//		}
//		featureMap.put(feature, value);
//	}

	protected void undoUpdateDescriptors() {
		undoSetResponsibleRole();

		undoUpdateTaskDescriptors();

		// restore old values
		//
		batchCommand.undo();

		// restore value for refreshable features that had been cleared
		//
		if (synchronize) {
			undoClearDescriptors();
		}
	}

	/**
	 * 
	 */
	private void clearDescriptors() {
		if (descriptorToOldRefreshableFeaturesMap == null) {
			descriptorToOldRefreshableFeaturesMap = new HashMap();
			if (!batchCommand.getDescriptorsToRefresh().isEmpty()) {
				for (Iterator iter = batchCommand.getDescriptorsToRefresh().iterator(); iter
						.hasNext();) {
					clearDescriptor((Descriptor) iter.next());
				}
				// descriptorsToRefresh.clear();
			}
		}
	}

	/**
	 * Updates affected task descriptors with new data from method content
	 */
	private void updateTaskDescriptors() {
		if (taskDescriptorsToUpdate != null) {
			// refresh steps from the associated task
			//
			if(synchFeatures.contains(UmaPackage.eINSTANCE.getTask_Steps())) {
				if (taskDescToNewStepsMap == null) {
					taskDescToNewStepsMap = new HashMap();
				}
				IConfigurationApplicator configApplicator = Providers
				.getConfigurationApplicator();
				for (Iterator iter = taskDescriptorsToUpdate.iterator(); iter
				.hasNext();) {
					TaskDescriptor taskDesc = (TaskDescriptor) iter.next();
					Task task = taskDesc.getTask();
					if (task != null && !((EObject) task).eIsProxy()) {
						List steps = (List) configApplicator.getReference(task
								.getPresentation(), task, UmaPackage.eINSTANCE
								.getContentDescription_Sections(), config);
						
						// add those steps to TaskDescriptor if they are not there
						// yet.
						//
						ArrayList newSteps = new ArrayList();
						taskDesc.getSelectedSteps().retainAll(steps);
						for (Iterator iter1 = steps.iterator(); iter1.hasNext();) {
							Object step = iter1.next();
							if (!taskDesc.getSelectedSteps().contains(step)) {
								newSteps.add(step);
							}
						}
						if (!newSteps.isEmpty()) {
							taskDesc.getSelectedSteps().addAll(newSteps);
							taskDescToNewStepsMap.put(taskDesc, newSteps);
//							System.out
//							.println("BSDropCommand.updateTaskDescriptors(): changed");
						}
					}
				}
			}
			
		}
	}

	private void undoUpdateTaskDescriptors() {
		if (taskDescToNewStepsMap != null) {
			for (Iterator iter = taskDescToNewStepsMap.entrySet().iterator(); iter
					.hasNext();) {
				Map.Entry entry = (Map.Entry) iter.next();
				TaskDescriptor taskDesc = (TaskDescriptor) entry.getKey();
				taskDesc.getSelectedSteps().removeAll(
						(Collection) entry.getValue());
			}
		}
	}

	/**
	 * Go thru all the work product descriptors of this activity and set the
	 * responsible role for them if there is any
	 */
	private void setResponsibleRoles() {
		if(!synchFeatures.contains(UmaPackage.eINSTANCE.getRole_ResponsibleFor())) {
			return;
		}
		
		if(refreshResponsibleForCommand != null) {
			refreshResponsibleForCommand.dispose();
		}
		refreshResponsibleForCommand = new BatchCommand(true);
 
		List brElements = activity.getBreakdownElements();
		List wpDescList = new ArrayList();
		List roleDescriptors = new ArrayList();
		for (Iterator itor = brElements.iterator(); itor.hasNext();) {
			Object obj = itor.next();
			if (obj instanceof WorkProductDescriptor) {
				wpDescList.add(obj);
			} else if (obj instanceof RoleDescriptor) {
				roleDescriptors.add(obj);
			}
		}

		for (Iterator itor = roleDescriptors.iterator(); itor.hasNext();) {
			RoleDescriptor roleDesc = (RoleDescriptor) itor.next();
			Role role = roleDesc.getRole();
			if (role != null) {				
				List responsibleWorkProducts = (List) Providers.getConfigurationApplicator().getReference(role, UmaPackage.eINSTANCE.getRole_ResponsibleFor(), config);
				List responsibleWPDList = new ArrayList();
				for (int j = wpDescList.size() - 1; j > -1; j--) {
					WorkProductDescriptor wpDesc = (WorkProductDescriptor) wpDescList
							.get(j);
//					RoleDescriptor responsibleRoleDesc = AssociationHelper.getResponsibleRoleDescriptor(wpDesc);
//					if (responsibleWorkProducts.contains(wpDesc
//							.getWorkProduct())
//							&&  responsibleRoleDesc != roleDesc) {
//						wpdToOldResponsibleRoleMap.put(wpDesc, responsibleRoleDesc);
////						wpDesc.setResponsibleRole(roleDesc);
//						roleDesc.getResponsibleFor().add(wpDesc);
//					}
					
					if(responsibleWorkProducts.contains(wpDesc.getWorkProduct())) {
						responsibleWPDList.add(wpDesc);
					}
				}				
				if(!roleDesc.getResponsibleFor().equals(responsibleWPDList)) {
					refreshResponsibleForCommand.addFeatureValues(roleDesc, UmaPackage.eINSTANCE.getRoleDescriptor_ResponsibleFor(), responsibleWPDList);
				}
			}
		}
		
		refreshResponsibleForCommand.execute();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.common.command.AbstractCommand#undo()
	 */
	public void undo() {
		undoNestedCommands();
		
		// remove any object that had been added to the default configuration
		//
		if (addedObjects != null && !addedObjects.isEmpty()) {
			MethodConfiguration config = process.getDefaultContext();
			for (Iterator iter = addedObjects.iterator(); iter.hasNext();) {
				Object element = iter.next();
				if (element instanceof MethodPlugin) {
					config.getMethodPluginSelection().remove(element);
				} else if (element instanceof MethodPackage) {
					config.getMethodPackageSelection().remove(element);
				}
			}
			addedToDefaultConfig = false;
		}

		undoUpdateDescriptors();

		doUndo();
	}

	/**
	 * 
	 */
	private void undoClearDescriptors() {
		if(descriptorToOldRefreshableFeaturesMap != null) {
			for (Iterator iter = descriptorToOldRefreshableFeaturesMap.entrySet()
					.iterator(); iter.hasNext();) {
				Map.Entry entry = (Map.Entry) iter.next();
				Descriptor desc = (Descriptor) entry.getKey();
				Map featureMap = (Map) entry.getValue();
				for (Iterator iterator = featureMap.entrySet().iterator(); iterator
				.hasNext();) {
					entry = (Map.Entry) iterator.next();
					desc
					.eSet((EStructuralFeature) entry.getKey(), entry
							.getValue());
				}
			}
		}
	}

	/**
	 * Undoes the change made by
	 * {@link #setResponsibleRoles() setResponsibleRoles() }
	 */
	private void undoSetResponsibleRole() {
//		if(wpdToOldResponsibleRoleMap != null && !wpdToOldResponsibleRoleMap.isEmpty()) {
//			for (Iterator iter = wpdToOldResponsibleRoleMap.entrySet().iterator(); iter
//			.hasNext();) {
//				Map.Entry entry = (Map.Entry) iter.next();
//				WorkProductDescriptor wpd = (WorkProductDescriptor) entry.getKey();
//				((RoleDescriptor) entry.getValue()).getResponsibleFor().remove(wpd);
//			}
//		}
		if(refreshResponsibleForCommand != null) {
			refreshResponsibleForCommand.undo();
		}
	}

	protected boolean prepare() {
		return true;
	}
	
	protected abstract void doExecute();

	protected abstract void doUndo();

	public static interface IExecutor {
		boolean preExecute();

		void doExcecute();

		void doUndo();
	}

}