| //------------------------------------------------------------------------------ |
| // Copyright (c) 2005, 2007 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.configuration; |
| |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.epf.library.edit.util.TngUtil; |
| import org.eclipse.epf.library.util.LibraryUtil; |
| import org.eclipse.epf.uma.Activity; |
| import org.eclipse.epf.uma.ContentCategory; |
| import org.eclipse.epf.uma.CustomCategory; |
| import org.eclipse.epf.uma.Discipline; |
| import org.eclipse.epf.uma.DisciplineGrouping; |
| import org.eclipse.epf.uma.Domain; |
| import org.eclipse.epf.uma.MethodConfiguration; |
| import org.eclipse.epf.uma.MethodElement; |
| import org.eclipse.epf.uma.MethodLibrary; |
| import org.eclipse.epf.uma.MethodPackage; |
| import org.eclipse.epf.uma.MethodPlugin; |
| import org.eclipse.epf.uma.ProcessComponent; |
| import org.eclipse.epf.uma.ProcessPackage; |
| import org.eclipse.epf.uma.RoleSet; |
| import org.eclipse.epf.uma.RoleSetGrouping; |
| import org.eclipse.epf.uma.Tool; |
| import org.eclipse.epf.uma.WorkProductType; |
| |
| /** |
| * Class managing configuration data calculation and cache |
| * |
| * @author Weiping Lu - Mar 20, 2007 |
| * @since 1.2 |
| */ |
| public class ConfigurationData { |
| |
| public static boolean active = false; //temp flag to be removed |
| |
| private MethodConfiguration config; |
| private Map<String, MethodElementData> substractedElemMap = new HashMap<String, MethodElementData>(); |
| private Map<String, MethodElementData> addedElemMap = new HashMap<String, MethodElementData>();; |
| private Map<String, ContentCategoryData> substractedCcMap = new HashMap<String, ContentCategoryData>(); |
| private Map<String, ContentCategoryData> addedCcMap = new HashMap<String, ContentCategoryData>(); |
| private boolean needUpdateConfigChange = true; |
| private Set<ContentCategory> changedContentCategorySet = new HashSet<ContentCategory>(); |
| |
| public ConfigurationData(MethodConfiguration config) { |
| this.config = config; |
| |
| Adapter configListener = new AdapterImpl() { |
| public void notifyChanged(Notification msg) { |
| int type = msg.getEventType(); |
| if ( type == Notification.ADD |
| || type == Notification.ADD_MANY |
| || type == Notification.REMOVE |
| || type == Notification.REMOVE_MANY) { |
| needUpdateConfigChange = true; |
| changedContentCategorySet = null; |
| } |
| } |
| }; |
| config.eAdapters().add(configListener); |
| } |
| |
| private void updateConfigChange() { |
| if (! needUpdateConfigChange) { |
| return; |
| } |
| |
| updateCcMap(substractedCcMap, collectAll(config.getSubtractedCategory()), substractedElemMap); |
| updateCcMap(addedCcMap, collectAll(config.getAddedCategory()), addedElemMap); |
| |
| needUpdateConfigChange = false; |
| } |
| |
| private Collection<ContentCategory> collectAll(Collection<ContentCategory> ccList) { |
| if (ccList == null || ccList.isEmpty()) { |
| return null; |
| } |
| Set<ContentCategory> all = new LinkedHashSet<ContentCategory>(); |
| collectAll(ccList, all); |
| return all; |
| } |
| |
| private void collectAll(Collection<ContentCategory> ccList, Set<ContentCategory> all) { |
| if (ccList == null || ccList.isEmpty()) { |
| return; |
| } |
| for (Iterator<ContentCategory> it = ccList.iterator(); it.hasNext();) { |
| ContentCategory cc = it.next(); |
| if (! all.contains(cc)) { |
| all.add(cc); |
| collectAll(getChildCC(cc), all); |
| } |
| } |
| } |
| |
| private Collection<ContentCategory> getChildCC(ContentCategory cc) { |
| if (cc instanceof CustomCategory) { |
| return ((CustomCategory) cc).getSubCategories(); |
| } |
| if (cc instanceof Discipline) { |
| return ((Discipline) cc).getSubdiscipline(); |
| } |
| if (cc instanceof DisciplineGrouping) { |
| return ((DisciplineGrouping) cc).getDisciplines(); |
| } |
| if (cc instanceof Domain) { |
| return ((Domain) cc).getSubdomains(); |
| } |
| if (cc instanceof RoleSet) { |
| return ((RoleSet) cc).getRoles(); |
| } |
| if (cc instanceof RoleSetGrouping) { |
| return ((RoleSetGrouping) cc).getRoleSets(); |
| } |
| if (cc instanceof Tool) { |
| return ((Tool) cc).getToolMentors(); |
| } |
| if (cc instanceof WorkProductType) { |
| return ((WorkProductType) cc).getWorkProducts(); |
| } |
| return null; |
| } |
| |
| |
| private void updateCcMap(Map<String, ContentCategoryData> map, Collection<ContentCategory> ccList, |
| Map<String, MethodElementData> ccDataOwner) { |
| if (ccList == null || ccList.isEmpty()) { |
| return; |
| } |
| Map<String, ContentCategoryData> newMap = new HashMap<String, ContentCategoryData>(); |
| |
| boolean changed = false; |
| for (Iterator<ContentCategory> it = ccList.iterator(); it.hasNext();) { |
| ContentCategory cc = it.next(); |
| ContentCategoryData ccData = map == null ? null : map.get(cc |
| .getGuid()); |
| if (ccData == null) { |
| ccData = new ContentCategoryData(cc, ccDataOwner); |
| changed = true; |
| } |
| newMap.put(cc.getGuid(), ccData); |
| } |
| |
| for (Iterator<Map.Entry<String, ContentCategoryData>> it = map |
| .entrySet().iterator(); it.hasNext();) { |
| Map.Entry<String, ContentCategoryData> entry = it.next(); |
| String guid = entry.getKey(); |
| ContentCategoryData ccData = entry.getValue(); |
| |
| if (!newMap.containsKey(guid)) { |
| changed = true; |
| incOrDecElemMap(ccDataOwner, getElements(ccData.getContentCategory()), false); |
| } |
| } |
| |
| if (changed) { |
| map = newMap; |
| } |
| } |
| |
| private void updateCcChanges() { |
| if (changedContentCategorySet == null) { |
| return; |
| } |
| for (Iterator<ContentCategory> it = changedContentCategorySet.iterator(); it.hasNext(); ) { |
| ContentCategory cc = it.next(); |
| updateCcChange(substractedCcMap, cc); |
| updateCcChange(addedCcMap, cc); |
| } |
| changedContentCategorySet = null; |
| } |
| |
| private void updateCcChange(Map<String, ContentCategoryData> map, ContentCategory cc) { |
| ContentCategoryData ccData = map == null ? null : map.get(cc.getGuid()); |
| if (ccData == null) { |
| return; |
| } |
| assert(ccData.getContentCategory() == cc); |
| ccData.updateElementMap(); |
| } |
| |
| //No duplicates in the returned elements |
| private Collection<MethodElement> getElements(ContentCategory cc) { |
| //return cc.getCategorizedElements(); |
| return null; |
| } |
| |
| private void incOrDecElemMap(Map<String, MethodElementData> map, |
| Collection<MethodElement> elements, boolean inc) { |
| for (Iterator<MethodElement> it = elements.iterator(); it.hasNext();) { |
| incOrDecElemMap(map, it.next(), inc); |
| } |
| } |
| |
| private void incOrDecElemMap(Map<String, MethodElementData> map, MethodElement element, |
| boolean inc) { |
| if (map == null) { |
| return; |
| } |
| |
| MethodElementData data = map.get(element.getGuid()); |
| if (data != null) { |
| assert (element == data.element); |
| assert (data.refCount > 0); |
| |
| if (inc) { |
| data.refCount++; |
| } else { |
| data.refCount--; |
| if (data.refCount == 0) { |
| map.remove(element.getGuid()); |
| } |
| } |
| return; |
| } else if (!inc) { |
| return; |
| } |
| |
| map.put(element.getGuid(), new MethodElementData(element)); |
| } |
| |
| public boolean isOwnerSelected(MethodElement element, boolean checkSubtracted) { |
| if (element == null) { |
| return false; |
| } |
| |
| if (ConfigurationHelper.isDescriptionElement(element)) { |
| return true; |
| } |
| |
| //refresh cache info |
| updateConfigChange(); |
| updateCcChanges(); |
| |
| // since UMA 1.0.4, configuration can have added categories |
| // and subtracted categories. The order of filtering is: |
| // 1. any element in the subtracted categories should be excluded |
| // 2. any element in the added categories should be included |
| // 3. any element not in the selected package or plugin should be excluded. |
| if ( checkSubtracted ) { |
| if (substractedElemMap.containsKey(element.getGuid())) { |
| return false; |
| } |
| } |
| |
| if (addedElemMap.containsKey(element.getGuid())) { |
| return true; |
| } |
| |
| // elements beyond configuration scope should be always visible |
| if ((element instanceof MethodLibrary) |
| || (element instanceof MethodConfiguration)) { |
| return true; |
| } else if (element instanceof MethodPlugin) { |
| List plugins = config.getMethodPluginSelection(); |
| return (plugins != null) && plugins.contains(element); |
| } else { |
| // if the ownerprocess can't show, can't accept |
| if (element instanceof Activity) { |
| Activity base = (Activity) ((Activity) element) |
| .getVariabilityBasedOnElement(); |
| if (base != null && base != element) { |
| MethodElement owningProc = TngUtil.getOwningProcess(base); |
| if ( owningProc != null && owningProc != element |
| && !ConfigurationHelper.inConfig(owningProc, config, checkSubtracted)) { |
| return false; |
| } |
| } |
| } |
| |
| EObject pkg = LibraryUtil.getSelectable(element); |
| |
| // accept global package if the plugin is in the configuration |
| if (pkg instanceof MethodPackage |
| && ConfigurationHelper.isGlobalPackage((MethodPackage) pkg)) { |
| MethodPlugin plugin = LibraryUtil.getMethodPlugin(pkg); |
| return ConfigurationHelper.inConfig(plugin, config, checkSubtracted); |
| } |
| |
| List pkgs = config.getMethodPackageSelection(); |
| if (pkgs == null) { |
| return false; |
| } |
| |
| // per Phong's request, for ProcessPackage, check the |
| // ProcessComponent parent instead |
| if (pkg instanceof ProcessPackage) { |
| while ((pkg != null) && !(pkg instanceof ProcessComponent) |
| && !pkgs.contains(pkg)) { |
| pkg = pkg.eContainer(); |
| } |
| } |
| |
| // if package not selected, return false |
| if ((pkg == null) || !pkgs.contains(pkg)) { |
| return false; |
| } |
| |
| return true; |
| } |
| } |
| |
| private class ContentCategoryData { |
| private ContentCategory ContentCategory; |
| private Map<String, MethodElement> elementMap = new HashMap<String, MethodElement>(); |
| private Map<String, MethodElementData> dataMap; |
| |
| public ContentCategoryData(ContentCategory cc, Map<String, MethodElementData> dataMap) { |
| ContentCategory = cc; |
| |
| Adapter ContentCategoryListener = new AdapterImpl() { |
| public void notifyChanged(Notification msg) { |
| int type = msg.getEventType(); |
| if ( type == Notification.ADD |
| || type == Notification.ADD_MANY |
| || type == Notification.REMOVE |
| || type == Notification.REMOVE_MANY) { |
| if (needUpdateConfigChange) { |
| return; |
| } |
| changedContentCategorySet.add(ContentCategory); |
| } |
| } |
| }; |
| ContentCategory.eAdapters().add(ContentCategoryListener); |
| |
| this.dataMap = dataMap; |
| updateElementMap(); |
| } |
| |
| public ContentCategory getContentCategory() { |
| return ContentCategory; |
| } |
| |
| public void updateElementMap() { |
| |
| Map<String, MethodElement> newMap = new HashMap<String, MethodElement>(); |
| Collection<MethodElement> elementList = getElements(ContentCategory); |
| |
| boolean changed = false; |
| for (Iterator<MethodElement> it = elementList.iterator(); it |
| .hasNext();) { |
| MethodElement element = it.next(); |
| if (elementMap == null |
| || !elementMap.containsKey(element.getGuid())) { |
| changed = true; |
| incOrDecElemMap(dataMap, element, true); |
| } |
| newMap.put(element.getGuid(), element); |
| } |
| |
| for (Iterator<Map.Entry<String, MethodElement>> it = elementMap |
| .entrySet().iterator(); it.hasNext();) { |
| Map.Entry<String, MethodElement> entry = it.next(); |
| String guid = entry.getKey(); |
| MethodElement element = entry.getValue(); |
| if (!newMap.containsKey(guid)) { |
| changed = true; |
| incOrDecElemMap(dataMap, element, false); |
| } |
| } |
| |
| if (changed) { |
| elementMap = newMap; |
| } |
| |
| } |
| |
| } |
| |
| private class MethodElementData { |
| public MethodElement element; |
| public int refCount; |
| |
| public MethodElementData(MethodElement e) { |
| element = e; |
| refCount = 1; |
| } |
| } |
| |
| } |