| //------------------------------------------------------------------------------ |
| // 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.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| 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.VariabilityElement; |
| import org.eclipse.epf.uma.VariabilityType; |
| 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>(); |
| private Map<String, ContentCategory> originalSubstracted; |
| private Map<String, ContentCategory> orignalAdded; |
| |
| public ConfigurationData(MethodConfiguration config) { |
| this.config = config; |
| |
| //-> Temp test |
| |
| |
| |
| |
| |
| //<- Temp test |
| |
| 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 boolean getUpdatingConfigChange() { |
| return originalSubstracted != null && orignalAdded != null; |
| } |
| |
| private void updateConfigChange() { |
| if (! needUpdateConfigChange) { |
| return; |
| } |
| if (getUpdatingConfigChange()) { |
| return; |
| } |
| |
| originalSubstracted = collectAll(config.getSubtractedCategory()); |
| orignalAdded = collectAll(config.getAddedCategory()); |
| |
| Map<String, ContentCategory> calSubstracted = handleReplacers(originalSubstracted, true); |
| Map<String, ContentCategory> calAdded = handleReplacers(orignalAdded, false); |
| handleContributors(calSubstracted, true); |
| handleContributors(calAdded, false); |
| |
| updateCcMap(addedCcMap, calSubstracted.values(), addedElemMap); |
| updateCcMap(substractedCcMap, calAdded.values(), substractedElemMap); |
| |
| needUpdateConfigChange = false; |
| |
| originalSubstracted = null; //not needed after update |
| orignalAdded = null; //not needed after update |
| } |
| |
| private void handleContributors(Map<String, ContentCategory> map, boolean substracted) { |
| if (! substracted) { |
| return; |
| } |
| List<ContentCategory> addedList = new ArrayList<ContentCategory>(); |
| |
| for (Iterator<ContentCategory> it = map.values().iterator(); it.hasNext();) { |
| ContentCategory cc = it.next(); |
| List<ContentCategory> contributers = ConfigurationHelper.getContributors(cc, config); |
| addedList.addAll(contributers); |
| } |
| for (Iterator<ContentCategory> it = addedList.iterator(); it.hasNext();) { |
| ContentCategory cc = it.next(); |
| map.put(cc.getGuid(), cc); |
| } |
| } |
| |
| private Map<String, ContentCategory> handleReplacers(Map<String, ContentCategory> originals, boolean substracted) { |
| if (originals == null || originals.isEmpty()) { |
| return originals; |
| } |
| |
| Map<String, ContentCategory> ret = new HashMap<String, ContentCategory>(); |
| |
| for (Iterator<ContentCategory> it = originals.values().iterator(); it.hasNext();) { |
| ContentCategory cc = it.next(); |
| VariabilityElement replacer = ConfigurationHelper.getReplacer(cc, config); |
| boolean toKeep = true; |
| if (replacer != null) { |
| toKeep = false; |
| |
| VariabilityElement nestedReplacer = replacer; |
| while (nestedReplacer != null) { |
| replacer = nestedReplacer; |
| nestedReplacer = ConfigurationHelper.getReplacer(nestedReplacer, config); |
| } |
| if (substracted) { |
| if (originalSubstracted.containsKey(replacer.getGuid())) { |
| toKeep = true; |
| } |
| } |
| } |
| if (toKeep) { |
| ret.put(cc.getGuid(), cc); |
| } |
| |
| } |
| return ret; |
| } |
| |
| private Map<String, ContentCategory> collectAll(Collection<ContentCategory> ccList) { |
| if (ccList == null || ccList.isEmpty()) { |
| return null; |
| } |
| Map<String, ContentCategory> all = new LinkedHashMap<String, ContentCategory>(); |
| collectAll(ccList, all); |
| return all; |
| } |
| |
| private void collectAll(Collection<ContentCategory> ccList, Map<String, ContentCategory> all) { |
| if (ccList == null || ccList.isEmpty()) { |
| return; |
| } |
| for (Iterator<ContentCategory> it = ccList.iterator(); it.hasNext();) { |
| ContentCategory cc = it.next(); |
| if (! all.containsKey(cc.getGuid())) { |
| all.put(cc.getGuid(), 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) { |
| } |
| if (cc instanceof RoleSetGrouping) { |
| return ((RoleSetGrouping) cc).getRoleSets(); |
| } |
| if (cc instanceof Tool) { |
| return ((Tool) cc).getToolMentors(); |
| } |
| if (cc instanceof WorkProductType) { |
| } |
| 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) { |
| Collection<MethodElement> elements = new ArrayList<MethodElement>(); |
| elements.addAll(cc.getAssets()); |
| elements.addAll(cc.getChecklists()); |
| elements.addAll(cc.getConceptsAndPapers()); |
| elements.addAll(cc.getExamples()); |
| elements.addAll(cc.getGuidelines()); |
| elements.addAll(cc.getSupportingMaterials()); |
| |
| if (cc instanceof CustomCategory) { |
| elements.addAll(((CustomCategory) cc).getSubCategories()); |
| elements.addAll(((CustomCategory) cc).getCategorizedElements()); |
| } |
| else if (cc instanceof Discipline) { |
| elements.addAll(((Discipline) cc).getReferenceWorkflows()); |
| elements.addAll(((Discipline) cc).getSubdiscipline()); |
| elements.addAll(((Discipline) cc).getTasks()); |
| } |
| else if (cc instanceof DisciplineGrouping) { |
| elements.addAll(((DisciplineGrouping) cc).getDisciplines()); |
| } |
| else if (cc instanceof Domain) { |
| elements.addAll(((Domain) cc).getSubdomains()); |
| elements.addAll(((Domain) cc).getWorkProducts()); |
| } |
| else if (cc instanceof RoleSet) { |
| elements.addAll(((RoleSet) cc).getRoles()); |
| } |
| else if (cc instanceof RoleSetGrouping) { |
| elements.addAll(((RoleSetGrouping) cc).getRoleSets()); |
| } |
| else if (cc instanceof Tool) { |
| elements.addAll(((Tool) cc).getToolMentors()); |
| } |
| else if (cc instanceof WorkProductType) { |
| elements.addAll(((WorkProductType) cc).getWorkProducts()); |
| } |
| |
| return getUniqueCollection(elements); |
| } |
| |
| private Collection<MethodElement> getUniqueCollection(Collection<MethodElement> elements) { |
| Collection<MethodElement> ret = new LinkedHashSet<MethodElement>(); |
| for (Iterator<MethodElement> it = elements.iterator(); it.hasNext();) { |
| MethodElement element = it.next(); |
| if (! ret.contains(element)) { |
| ret.add(element); |
| } |
| } |
| return ret; |
| } |
| |
| |
| private void incOrDecElemMap(Map<String, MethodElementData> map, |
| Collection<MethodElement> elements, boolean inc) { |
| if (elements == null || elements.isEmpty()) { |
| return; |
| } |
| 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; |
| } |
| |
| if (getUpdatingConfigChange()) { |
| if (originalSubstracted.containsKey(element.getGuid()) || |
| orignalAdded.containsKey(element.getGuid())) { |
| return true; |
| } |
| } else { |
| 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; |
| } else if (element instanceof VariabilityElement){ |
| if (contributedBaseInSubstracted((VariabilityElement) element)) { |
| 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 boolean contributedBaseInSubstracted(VariabilityElement ve) { |
| if (ve.getVariabilityType() != VariabilityType.CONTRIBUTES_LITERAL) { |
| return false; |
| } |
| |
| VariabilityElement base = ve.getVariabilityBasedOnElement(); |
| if (base == null) { |
| return false; |
| } |
| |
| if (substractedElemMap.containsKey(base.getGuid())) { |
| return true; |
| } |
| |
| if (ConfigurationHelper.inConfig(base, config, true)) { |
| return contributedBaseInSubstracted(base); |
| } |
| |
| return false; |
| } |
| |
| 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.SET) { |
| //to do: check any variability change |
| //msg.getFeature(); |
| needUpdateConfigChange = true; |
| } |
| |
| 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; |
| } |
| } |
| |
| } |