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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.eclipse.emf.common.util.AbstractTreeIterator;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.epf.library.edit.command.AddToCategoryCommand;
import org.eclipse.epf.library.edit.command.IActionManager;
import org.eclipse.epf.library.edit.command.IResourceAwareCommand;
import org.eclipse.epf.library.edit.command.RemoveFromCategoryCommand;
import org.eclipse.epf.library.edit.command.SetOppositeFeatureCommand;
import org.eclipse.epf.library.edit.util.MethodElementUtil;
import org.eclipse.epf.library.edit.util.Misc;
import org.eclipse.epf.library.edit.util.ModelStructure;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.ContentCategory;
import org.eclipse.epf.uma.ContentElement;
import org.eclipse.epf.uma.ContentPackage;
import org.eclipse.epf.uma.CustomCategory;
import org.eclipse.epf.uma.DescribableElement;
import org.eclipse.epf.uma.Discipline;
import org.eclipse.epf.uma.DisciplineGrouping;
import org.eclipse.epf.uma.Domain;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.Role;
import org.eclipse.epf.uma.RoleSet;
import org.eclipse.epf.uma.RoleSetGrouping;
import org.eclipse.epf.uma.Task;
import org.eclipse.epf.uma.Tool;
import org.eclipse.epf.uma.ToolMentor;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.VariabilityElement;
import org.eclipse.epf.uma.VariabilityType;
import org.eclipse.epf.uma.WorkProduct;
import org.eclipse.epf.uma.WorkProductType;
import org.eclipse.epf.uma.util.AssociationHelper;
import org.eclipse.epf.uma.util.UmaUtil;


/**
 * This class contains all the routines used by the tool editor to manipulate
 * the associations of a method element in the library with other categories or
 * method elements
 * 
 * Use getAvailableXXX to retrieve a pick list of categories, method element for
 * a given method element. Use addToXXX to establish a new association. Use
 * removeFromXXX to remove an exisiting association.
 * 
 * Call releaseCategories() before the editor closes, passing in all the
 * categories that you have retrieved with getAvailableXXX() for the editor.
 * 
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class LibraryManager {

	private static Comparator nameComparator = new Comparator() {

		public int compare(Object o1, Object o2) {
			if (o1 instanceof MethodElement && o2 instanceof MethodElement) {
				return ((MethodElement) o1).getName().compareTo(
						((MethodElement) o2).getName());
			}

			return 0;
		}

	};

	private static LibraryManager instance = null;

	public static final LibraryManager getInstance() {
		if (instance == null) {
			synchronized (LibraryManager.class) {
				if (instance == null) {
					instance = new LibraryManager();
				}
			}
		}
		return instance;
	}

	protected LibraryManager() {
	}

	// //////
	// Begin of code moved from MethodElementUtil and made private
	// //////
	/**
	 * Return categories in the model, given particular object and class
	 * 
	 * @param object
	 * @param class
	 * @param String[]
	 * @return
	 */
	public List getAvailableCategories(MethodElement object, Class classType,
			String[] path) {
		List itemList = new ArrayList();
		MethodPlugin model = UmaUtil.getMethodPlugin((EObject) object);

		if (model != null) {
			// add all categories from the current plugin
			//
			ContentPackage disciplinePkg = UmaUtil.findContentPackage(model,
					path);
			if (disciplinePkg != null) {
				EList contentCats = (EList) disciplinePkg.getContentElements();
				for (int i = 0; i < contentCats.size(); i++) {
					Object element = contentCats.get(i);

					if (classType.isInstance(element) && element != object) {
						itemList.add(element);
					}
				}
			}

			// add all categories from base plugins that are not extended
			//
			List list = Misc.getAllBase(model);
			// list.add(0, model);
			for (int j = 0; j < list.size(); j++) {
				ContentPackage parentDisciplines = UmaUtil.findContentPackage(
						((MethodPlugin) list.get(j)), path);
				if (parentDisciplines != null) {
					EList contentCats = (EList) parentDisciplines
							.getContentElements();
					for (int i = 0; i < contentCats.size(); i++) {
						Object element = contentCats.get(i);

						if (classType.isInstance(element)) {
							VariabilityElement item = (VariabilityElement) element;
							if (item.getVariabilityBasedOnElement() == null) {
								// make sure the itemList doesn't have a
								// category that extends this
								int k;
								for (k = 0; k < itemList.size(); k++) {
									if (((VariabilityElement) itemList.get(k))
											.getVariabilityBasedOnElement() == item)
										break;
								}
								if (k == itemList.size())
									itemList.add(item);

								// itemList.add(item);
							}
						}
					}
				}
			}
		}

		Collections.sort(itemList, nameComparator);

		return itemList;
	}

	// ////////
	// End of code moved from MethodElementUtil
	// ////////

	/**
	 * Gets all disciplines that are available to the given MethodElement
	 * object.
	 * 
	 * @param e
	 * @return list of Discipline objects
	 */
	public List getAvailableDisciplines(MethodElement e) {
		return getAvailableCategories(e, Discipline.class,
				ModelStructure.DEFAULT_DISCIPLINE_DEF_PATH);
	}

	/**
	 * Gets all domains that are available to the given MethodElement object.
	 * 
	 * @param e
	 * @return list of Domain objects
	 */
	public List getAvailableDomains(MethodElement e) {
		List domains = getAvailableCategories(e, Domain.class,
				ModelStructure.DEFAULT_DOMAIN_PATH);

		// get all the domains including subdomains
		//
		List allDomains = new ArrayList();
		for (Iterator iter = domains.iterator(); iter.hasNext();) {
			Domain domain = (Domain) iter.next();
			Iterator domainTree = new AbstractTreeIterator(domain, true) {

				protected Iterator getChildren(Object object) {
					return ((Domain) object).getSubdomains().iterator();
				}

			};
			while (domainTree.hasNext()) {
				allDomains.add(domainTree.next());
			}
		}

		Collections.sort(allDomains, nameComparator);

		return allDomains;
	}

	/**
	 * Gets all work product types that are available to the given MethodElement
	 * object.
	 * 
	 * @param e
	 * @return list of WorkProductType objects
	 */
	public List getAvailableWorkProductTypes(MethodElement e) {
		return getAvailableCategories(e, WorkProductType.class,
				ModelStructure.DEFAULT_WORK_PRODUCT_TYPE_PATH);
	}

	/**
	 * Gets all role sets that are available to the given MethodElement object.
	 * 
	 * @param e
	 * @return list of RoleSet objects
	 */
	public List getAvailableRoleSets(MethodElement e) {
		return getAvailableCategories(e, RoleSet.class,
				ModelStructure.DEFAULT.roleSetPath);
	}

	/**
	 * Gets all custom categories that are available to the given MethodElement
	 * object.
	 * 
	 * @param e
	 * @return list of RoleSet objects
	 */
	public List getAvailableCustomCategories(MethodElement e) {
		List categories = getAvailableCategories(e, CustomCategory.class,
				ModelStructure.DEFAULT_CUSTOM_CATEGORY_PATH);
		// filter out the root custom categories
		//
		for (Iterator iter = categories.iterator(); iter.hasNext();) {
			if (TngUtil.isRootCustomCategory((CustomCategory) iter.next())) {
				iter.remove();
			}
		}
		return categories;
	}

	/**
	 * Gets all tools that are available to the given MethodElement object.
	 * 
	 * @param e
	 * @return list of Tool objects
	 */
	public List getAvailableTools(MethodElement e) {
		return getAvailableCategories(e, Tool.class,
				ModelStructure.DEFAULT.toolPath);
	}

	/**
	 * Gets all discipline groupings that are available to the given
	 * MethodElement object.
	 * 
	 * @param e
	 * @return list of RoleSet objects
	 */
	public List getAvailableDisciplineGroupings(MethodElement e) {
		return getAvailableCategories(e, DisciplineGrouping.class,
				ModelStructure.DEFAULT.disciplineDefinitionPath);
	}

	/**
	 * Gets all role set groupings that are available to the given MethodElement
	 * object.
	 * 
	 * @param e
	 * @return list of RoleSet objects
	 */
	public List getAvailableRoleSetGroupings(MethodElement e) {
		return getAvailableCategories(e, RoleSetGrouping.class,
				ModelStructure.DEFAULT.roleSetPath);
	}

	// public Discipline addToDiscipline(Discipline discipline, Task task) {
	// return (Discipline) addToCategory(discipline, task,
	// UmaPackage.eINSTANCE.getDiscipline_Tasks(),
	// ModelStructure.DEFAULT.disciplineDefinitionPath, true);
	// }

	public Discipline addToDiscipline(IActionManager actionMgr,
			Discipline discipline, Task task) {
		return (Discipline) addToCategory(actionMgr, discipline, task,
				UmaPackage.eINSTANCE.getDiscipline_Tasks(),
				ModelStructure.DEFAULT.disciplineDefinitionPath, true);
	}

	// public Domain addToDomain(Domain domain, WorkProduct wp) {
	// return (Domain) addToCategory(domain, wp,
	// UmaPackage.eINSTANCE.getDomain_WorkProducts(),
	// ModelStructure.DEFAULT.domainPath, true);
	// }

	public Domain addToDomain(IActionManager actionMgr, Domain domain,
			WorkProduct wp) {
		return (Domain) addToCategory(actionMgr, domain, wp,
				UmaPackage.eINSTANCE.getDomain_WorkProducts(),
				ModelStructure.DEFAULT.domainPath, true);
	}

	// public WorkProductType addToWorkProductType(WorkProductType wpType,
	// WorkProduct wp) {
	// return (WorkProductType) addToCategory(wpType, wp,
	// UmaPackage.eINSTANCE.getWorkProductType_WorkProducts(),
	// ModelStructure.DEFAULT.workProductTypePath, true);
	// }

	public WorkProductType addToWorkProductType(IActionManager actionMgr,
			WorkProductType wpType, WorkProduct wp) {
		return (WorkProductType) addToCategory(actionMgr, wpType, wp,
				UmaPackage.eINSTANCE.getWorkProductType_WorkProducts(),
				ModelStructure.DEFAULT.workProductTypePath, true);
	}

	// public CustomCategory addToCustomCategory(CustomCategory
	// userDefinedCategory, MethodElement element) {
	// if(element instanceof ContentElement) {
	// return (CustomCategory) addToCategory(userDefinedCategory, element,
	// UmaPackage.eINSTANCE.getCustomCategory_CategorizedElements(),
	// ModelStructure.DEFAULT.customCategoryPath, true);
	// }
	// else if(element instanceof ContentCategory) {
	// return (CustomCategory) addToCategory(userDefinedCategory, element,
	// UmaPackage.eINSTANCE.getCustomCategory_SubCategories(),
	// ModelStructure.DEFAULT.customCategoryPath, true);
	// }
	// return null;
	// }

	public CustomCategory addToCustomCategory(IActionManager actionMgr,
			CustomCategory userDefinedCategory, MethodElement element) {
		if (element instanceof DescribableElement) {
			return (CustomCategory) addToCategory(actionMgr,
					userDefinedCategory, element, UmaPackage.eINSTANCE
							.getCustomCategory_CategorizedElements(),
					ModelStructure.DEFAULT.customCategoryPath, true);
		}

		// if(element instanceof ContentElement) {
		// return (CustomCategory) addToCategory(actionMgr, userDefinedCategory,
		// element,
		// UmaPackage.eINSTANCE.getCustomCategory_CategorizedElements(),
		// ModelStructure.DEFAULT.customCategoryPath, true);
		// }
		// else if(element instanceof ContentCategory) {
		// return (CustomCategory) addToCategory(actionMgr, userDefinedCategory,
		// element, UmaPackage.eINSTANCE.getCustomCategory_SubCategories(),
		// ModelStructure.DEFAULT.customCategoryPath, true);
		// }
		// else if(element instanceof Process){
		// return (CustomCategory) addToCategory(actionMgr, userDefinedCategory,
		// element, UmaPackage.eINSTANCE.getCustomCategory_ProcessElements(),
		// ModelStructure.DEFAULT.customCategoryPath, true);
		// }

		return null;
	}

	// public Tool addToTool(Tool tool, ToolMentor toolMentor) {
	// return (Tool) addToCategory(tool, toolMentor,
	// UmaPackage.eINSTANCE.getTool_ToolMentors(),
	// ModelStructure.DEFAULT.toolPath, true);
	// }

	public Tool addToTool(IActionManager actionMgr, Tool tool,
			ToolMentor toolMentor) {
		return (Tool) addToCategory(actionMgr, tool, toolMentor,
				UmaPackage.eINSTANCE.getTool_ToolMentors(),
				ModelStructure.DEFAULT.toolPath, true);
	}

	// public RoleSet addToRoleSet(RoleSet roleSet, Role role) {
	// return (RoleSet) addToCategory(roleSet, role,
	// UmaPackage.eINSTANCE.getRoleSet_Roles(),
	// ModelStructure.DEFAULT.roleSetPath, true);
	// }

	public RoleSet addToRoleSet(IActionManager actionMgr, RoleSet roleSet,
			Role role) {
		return (RoleSet) addToCategory(actionMgr, roleSet, role,
				UmaPackage.eINSTANCE.getRoleSet_Roles(),
				ModelStructure.DEFAULT.roleSetPath, true);
	}

	/**
	 * Adds a method element to the category. This method might create a new
	 * contributor to the given category in the method element's plugin if the
	 * given category belongs to the base plugin of the method element's plugin.
	 * 
	 * @param actionMgr
	 * @param category
	 * @param element
	 * @param feature
	 * @param categoryPkgPath
	 * @param checkForExistingContributor
	 * @return the actual category that the given method element is added to
	 */
	public ContentCategory addToCategory(IActionManager actionMgr,
			ContentCategory category, MethodElement element,
			EStructuralFeature feature, String[] categoryPkgPath,
			boolean checkForExistingContributor) {
		AddToCategoryCommand cmd = new AddToCategoryCommand(category, element,
				feature, categoryPkgPath, checkForExistingContributor);
		actionMgr.execute(cmd);
		return (ContentCategory) ((List) cmd.getAffectedObjects()).get(0);
	}

//	public void setResponsibleRole(IActionManager actionMgr, WorkProduct wp,
//			Role role) {
//		IResourceAwareCommand cmd = new SetOppositeFeatureCommand(wp,
//				AssociationHelper.WorkProduct_ResponsibleRole, role);
//		actionMgr.execute(cmd);
//	}

	/**
	 * Removes the method element from the category. This method might delete
	 * the category if it is a contributor and became empty after the removal of
	 * the method element.
	 * 
	 * @param category
	 * @param element
	 * @param feature
	 *            feature that contains the given method element
	 * @param contentPkgPath
	 * @return true if the method element is successfully removed, false
	 *         otherwise.
	 */
	public boolean removeFromCategory(IActionManager actionMgr,
			ContentCategory category, MethodElement element,
			EStructuralFeature feature, String[] contentPkgPath) {
		IResourceAwareCommand cmd = new RemoveFromCategoryCommand(category,
				element, feature, contentPkgPath);
		actionMgr.execute(cmd);
		return !cmd.getResult().isEmpty();
	}

	/**
	 * Removes the method element from the category. This method might delete
	 * the category if it is a contributor and became empty after the removal of
	 * the method element.
	 * 
	 * @param category
	 * @param element
	 * @param feature
	 *            feature that contains the given method element
	 * @param elementFeatures
	 *            features to check for emptiness before deleting the category
	 * @param contentPkgPath
	 * @return true if the method element is successfully removed, false
	 *         otherwise.
	 */
	// public boolean removeFromCategory(ContentCategory category, MethodElement
	// element, EStructuralFeature feature, EStructuralFeature[]
	// elementFeatures, String[] contentPkgPath) {
	// boolean ret;
	// boolean empty = false;
	// MethodPlugin categoryPlugin = UmaUtil.getMethodPlugin(category);
	// MethodPlugin elementPlugin = UmaUtil.getMethodPlugin(element);
	// ContentCategory usedCategory;
	// if(categoryPlugin != elementPlugin) {
	// usedCategory =
	// TngUtil.findContributor(UmaUtil.findContentPackage(elementPlugin,
	// contentPkgPath), category);
	// if(usedCategory == null) return false;
	// }
	// else {
	// usedCategory = category;
	// }
	// if(feature.isMany()) {
	// Collection collection = (Collection) usedCategory.eGet(feature);
	// ret = collection.remove(element);
	// empty = true;
	// //TODO: need revisit to find better way to remove the empty contributors
	// without adding to much work on in the UI code
	// //
	// // for (int i = 0; i < elementFeatures.length; i++) {
	// // collection = (Collection) category.eGet(elementFeatures[i]);
	// // if(!collection.isEmpty()) {
	// // empty = false;
	// // break;
	// // }
	// // }
	// }
	// else {
	// usedCategory.eSet(feature, null);
	// ret = true;
	// }
	// // if(category.getVariabilityBasedOnElement() != null && empty) {
	// // EcoreUtil.remove(category);
	// // }
	// return ret;
	// }
	// public boolean removeFromDiscipline(Discipline discipline, Task task) {
	// EStructuralFeature feature = UmaPackage.eINSTANCE.getDiscipline_Tasks();
	// return removeFromCategory(discipline, task, feature, new
	// EStructuralFeature[] { feature },
	// ModelStructure.DEFAULT.disciplineDefinitionPath);
	// }
	public boolean removeFromDiscipline(IActionManager actionMgr,
			Discipline discipline, Task task) {
		EStructuralFeature feature = UmaPackage.eINSTANCE.getDiscipline_Tasks();
		return removeFromCategory(actionMgr, discipline, task, feature,
				ModelStructure.DEFAULT.disciplineDefinitionPath);
	}

	// public boolean removeFromDomain(Domain domain, WorkProduct wp) {
	// EStructuralFeature feature =
	// UmaPackage.eINSTANCE.getDomain_WorkProducts();
	// return removeFromCategory(domain, wp, feature, new EStructuralFeature[] {
	// feature }, ModelStructure.DEFAULT.domainPath);
	// }

	public boolean removeFromDomain(IActionManager actionMgr, Domain domain,
			WorkProduct wp) {
		EStructuralFeature feature = UmaPackage.eINSTANCE
				.getDomain_WorkProducts();
		return removeFromCategory(actionMgr, domain, wp, feature,
				ModelStructure.DEFAULT.domainPath);
	}

	// public boolean removeFromWorkProductType(WorkProductType wpType,
	// WorkProduct wp) {
	// EStructuralFeature feature =
	// UmaPackage.eINSTANCE.getWorkProductType_WorkProducts();
	// return removeFromCategory(wpType, wp, feature, new EStructuralFeature[] {
	// feature }, ModelStructure.DEFAULT.workProductTypePath);
	// }

	public boolean removeFromWorkProductType(IActionManager actionMgr,
			WorkProductType wpType, WorkProduct wp) {
		EStructuralFeature feature = UmaPackage.eINSTANCE
				.getWorkProductType_WorkProducts();
		return removeFromCategory(actionMgr, wpType, wp, feature,
				ModelStructure.DEFAULT.workProductTypePath);
	}

	// public boolean removeFromRoleSet(RoleSet roleSet, Role role) {
	// EStructuralFeature feature = UmaPackage.eINSTANCE.getRoleSet_Roles();
	// return removeFromCategory(roleSet, role, feature, new
	// EStructuralFeature[] { feature }, ModelStructure.DEFAULT.roleSetPath);
	// }

	public boolean removeFromRoleSet(IActionManager actionMgr, RoleSet roleSet,
			Role role) {
		EStructuralFeature feature = UmaPackage.eINSTANCE.getRoleSet_Roles();
		return removeFromCategory(actionMgr, roleSet, role, feature,
				ModelStructure.DEFAULT.roleSetPath);
	}

	// public boolean removeFromTool(Tool tool, ToolMentor toolMentor) {
	// EStructuralFeature feature = UmaPackage.eINSTANCE.getTool_ToolMentors();
	// return removeFromCategory(tool, toolMentor, feature, new
	// EStructuralFeature[] { feature }, ModelStructure.DEFAULT.toolPath);
	// }

	public boolean removeFromTool(IActionManager actionMgr, Tool tool,
			ToolMentor toolMentor) {
		EStructuralFeature feature = UmaPackage.eINSTANCE.getTool_ToolMentors();
		return removeFromCategory(actionMgr, tool, toolMentor, feature,
				ModelStructure.DEFAULT.toolPath);
	}

	// public boolean removeFromCustomCategory(CustomCategory
	// userDefinedCategory, MethodElement e) {
	// EStructuralFeature contentElementFeature =
	// UmaPackage.eINSTANCE.getCustomCategory_CategorizedElements();
	// EStructuralFeature contentCategoryFeature =
	// UmaPackage.eINSTANCE.getCustomCategory_SubCategories();
	// EStructuralFeature[] features = { contentElementFeature,
	// contentCategoryFeature };
	// if(e instanceof ContentElement) {
	// return removeFromCategory(userDefinedCategory, e, contentElementFeature,
	// features, ModelStructure.DEFAULT.customCategoryPath);
	// }
	// else if(e instanceof ContentCategory) {
	// return removeFromCategory(userDefinedCategory, e, contentCategoryFeature,
	// features, ModelStructure.DEFAULT.customCategoryPath);
	// }
	// return false;
	// }

	public boolean removeFromCustomCategory(IActionManager actionMgr,
			CustomCategory userDefinedCategory, MethodElement e) {
		if (e instanceof DescribableElement) {
			EStructuralFeature feature = UmaPackage.eINSTANCE
					.getCustomCategory_CategorizedElements();
			return removeFromCategory(actionMgr, userDefinedCategory, e,
					feature, ModelStructure.DEFAULT.customCategoryPath);
		}
		return false;
	}

	/**
	 * Call this method before the editor closes, passing in all the categories
	 * that you have retrieved with getAvailableXXX() for the editor
	 * 
	 * @param consumer
	 * @param categories
	 */
	public void releaseCategories(MethodElement consumer, Collection categories) {
		MethodPlugin consumerPlugin = UmaUtil.getMethodPlugin(consumer);
		for (Iterator iter = categories.iterator(); iter.hasNext();) {
			ContentCategory category = (ContentCategory) iter.next();
			MethodPlugin categoryPlugin = UmaUtil.getMethodPlugin(category);
			if (category.getVariabilityBasedOnElement() != null
					&& categoryPlugin == consumerPlugin
					&& TngUtil.isEmpty(category)) {
				EcoreUtil.remove(category);
				if (category instanceof CustomCategory) {
					TngUtil.getRootCustomCategory(categoryPlugin)
							.getCategorizedElements().remove(category);
				}
			} else {
				String[] path = getCategoryPackagePath(category);
				// path can be null if the category is deleted
				//
				if (path != null) {
					ContentPackage categoryPkg = UmaUtil.findContentPackage(
							consumerPlugin, path);
					ContentCategory contributor = TngUtil.findContributor(
							categoryPkg, category);
					if (contributor != null && TngUtil.isEmpty(contributor)) {
						EcoreUtil.remove(contributor);
					}
				}
			}
		}
	}

	public List getAvailableTasks(ContentCategory category) {
		return getAvailableContentElements(Task.class, category, false);
	}

	public List getAvailableWorkProducts(ContentCategory category) {
		// // special handling to disallow assigment of work products to domain
		// with subdomains
		// //
		// if(category instanceof Domain &&
		// !((Domain)category).getSubdomains().isEmpty()) {
		// return Collections.EMPTY_LIST;
		// }

		return getAvailableContentElements(WorkProduct.class, category, false);
	}

	public List getAvailableRoles(ContentCategory category) {
		return getAvailableContentElements(Role.class, category, false);
	}

	/**
	 * Gets all content elements that are available to the given content element
	 * to reference to
	 * 
	 * @param methodElementType
	 *            concrect class of the the given content element.
	 * @param element
	 * @param localOnly
	 *            if true, only the elements in the given element's plugin are
	 *            included in the result.
	 * @return
	 */
	public List getAvailableContentElements(Class methodElementType,
			ContentElement element, boolean localOnly) {
		List elementList = new ArrayList();
		List modelList;
		if (localOnly) {
			modelList = Collections.singletonList(UmaUtil
					.getMethodPlugin(element));
		} else {
			modelList = MethodElementUtil.getAllModels(element);
		}
		int size = modelList.size();
		for (int i = 0; i < size; i++) {
			MethodPlugin model = (MethodPlugin) modelList.get(i);
			ContentPackage coreContentPkg = UmaUtil.findContentPackage(model,
					ModelStructure.DEFAULT.coreContentPath);
			if (coreContentPkg == null)
				continue;
			TreeIterator iterator = coreContentPkg.eAllContents();

			while (iterator.hasNext()) {
				Object obj = (Object) iterator.next();
				if (methodElementType.isInstance(obj) && obj != element) {
					elementList.add(obj);
				}
			}
		}

		Collections.sort(elementList, nameComparator);

		return elementList;
	}

	public List getAvailableTasks(Role role) {
		// While editing a role, you only can assign this role to a task in the
		// same plugin.
		// Othewise you will modify the base plugin.
		//
		return getAvailableContentElements(Task.class, role, true);
	}

	public List getAvailableTasks(WorkProduct wp) {
		// While editing a work product, you only can assign this work product
		// to a task in the same plugin.
		// Othewise you will modify the base plugin.
		//
		return getAvailableContentElements(Task.class, wp, true);
	}

	public List getAvailableRoles(WorkProduct wp) {
		// While editing a work product, you only can assign this work product
		// to a role in the same plugin.
		// Othewise you will modify the base plugin.
		//
		return getAvailableContentElements(Role.class, wp, true);
	}

	/**
	 * @param role
	 * @return
	 */
	public List getAvailableWorkProducts(Role role) {
		return getAvailableContentElements(WorkProduct.class, role, false);
	}

	/**
	 * @param task
	 * @return
	 */
	public List getAvailableWorkProducts(Task task) {
		return getAvailableContentElements(WorkProduct.class, task, false);
	}

	public List getAvailableRoles(Task task) {
		return getAvailableContentElements(Role.class, task, false);
	}

	public List getStepList(Task task) {
		if (task.getVariabilityType() == VariabilityType.EXTENDS_LITERAL
				|| task.getVariabilityType() == VariabilityType.CONTRIBUTES_LITERAL) {
			VariabilityElement base = TngUtil.getBase(task);
			final List types = new ArrayList();
			if (types.isEmpty()) {
				synchronized (types) {
					if (types.isEmpty()) {
						types.add(VariabilityType.CONTRIBUTES_LITERAL);
						types.add(VariabilityType.EXTENDS_LITERAL);
					}
				}
			}
			Iterator extended = new AbstractTreeIterator(base) {
				protected Iterator getChildren(Object object) {
					List list = AssociationHelper
							.getImmediateVarieties((VariabilityElement) object);
					if (list == null || list.isEmpty())
						Collections.EMPTY_LIST.iterator();
					int size = list.size();
					List outputList = new ArrayList();
					for (int i = 0; i < size; i++) {
						VariabilityElement e = (VariabilityElement) list.get(i);
						if (types.contains(e.getVariabilityType())) {
							outputList.add(e);
						}
					}
					return outputList.iterator();
				}
			};
			for (; extended.hasNext();) {
				Task t = (Task) extended.next();
				t.getPresentation().getSections();
			}
		}
		return null;
	}

	public List getExtended(VariabilityElement base, Collection variabilityTypes) {
		List list = new ArrayList();
		getExtended(list, base, variabilityTypes);
		return list;
	}

	private static void getExtended(List outputList, VariabilityElement base,
			Collection variabilityTypes) {
		List list = AssociationHelper.getImmediateVarieties(base);
		if (list == null || list.isEmpty())
			return;
		int size = list.size();
		for (int i = 0; i < size; i++) {
			VariabilityElement e = (VariabilityElement) list.get(i);
			if (variabilityTypes.contains(e.getVariabilityType())) {
				outputList.add(e);
			}
		}
		for (int i = 0; i < size; i++) {
			VariabilityElement e = (VariabilityElement) list.get(i);
			if (variabilityTypes.contains(e.getVariabilityType())) {
				getExtended(outputList, base, variabilityTypes);
			}
		}

	}

	private static String[] getCategoryPackagePath(ContentCategory category) {
		EObject parent = null;
		for (parent = category.eContainer(); parent != null
				&& !(parent instanceof ContentPackage); parent = parent
				.eContainer())
			;
		if (parent == null)
			return null;
		ContentPackage categoryPkg = (ContentPackage) parent;
		return Misc.getPathRelativeToPlugin(categoryPkg);
	}

}
