| //------------------------------------------------------------------------------ |
| // 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.edit.validation; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.eclipse.epf.library.edit.LibraryEditPlugin; |
| import org.eclipse.epf.library.edit.util.TngUtil; |
| import org.eclipse.epf.uma.Activity; |
| import org.eclipse.epf.uma.CustomCategory; |
| import org.eclipse.epf.uma.Deliverable; |
| import org.eclipse.epf.uma.MethodElement; |
| import org.eclipse.epf.uma.Practice; |
| import org.eclipse.epf.uma.VariabilityElement; |
| import org.eclipse.epf.uma.VariabilityType; |
| import org.eclipse.epf.uma.util.AssociationHelper; |
| |
| /** |
| * This class handles upward reachable info for circular dependency checking |
| * |
| * @author Weiping Lu |
| * @since 1.2 |
| */ |
| public class UpwardReachableInfo implements IDependencyInfo { |
| |
| private DependencyInfoMgr mgr; |
| private MethodElement element; |
| private HashMap parentMap; |
| private boolean complete = false; |
| private boolean debug = false; |
| |
| protected UpwardReachableInfo(DependencyInfoMgr mgr, MethodElement element) { |
| this.mgr = mgr; |
| this.element = element; |
| } |
| |
| public boolean isComplete() { |
| return complete; |
| } |
| |
| public void build(boolean checkCircular) { |
| if (debug) { |
| System.out.println("LD> build: " + this); //$NON-NLS-1$ |
| } |
| buildInner(checkCircular); |
| } |
| |
| private void buildInner(boolean checkCircular) { |
| Object info = mgr.getProcessedInfo(element); |
| if (info != null) { |
| if (info == this && complete) { |
| return; |
| } |
| throw new RuntimeException("Internal error in buildInner: " + info); //$NON-NLS-1$ |
| } |
| mgr.addToProcessed(this); |
| if (debug) { |
| System.out.println("LD> buildInner: " + this); //$NON-NLS-1$ |
| System.out.println(""); //$NON-NLS-1$ |
| } |
| buildInner_(checkCircular); |
| } |
| |
| private void buildInner_(boolean checkCircular) { |
| List parents = getMixedParentList(); |
| int sz = parents == null ? 0 : parents.size(); |
| if (sz == 0) { |
| complete = true; |
| return; |
| } |
| |
| parentMap = new HashMap(); |
| List processedParents = null; |
| for (int i = 0; i< sz; i++) { |
| MethodElement parentElem = (MethodElement) parents.get(i); |
| UpwardReachableInfo pinfo = (UpwardReachableInfo) mgr.getProcessedInfo(parentElem); |
| if (pinfo == null){ |
| pinfo = new UpwardReachableInfo(mgr, parentElem); |
| parentMap.put(parentElem.getGuid(), pinfo); |
| pinfo.buildInner(checkCircular); |
| } else { |
| parentMap.put(parentElem.getGuid(), pinfo); |
| if (checkCircular) { |
| if (processedParents == null) { |
| processedParents = new ArrayList(); |
| } |
| processedParents.add(pinfo); |
| } |
| } |
| } |
| complete = true; |
| |
| if (checkCircular && processedParents != null) { |
| for (int i=0; i<processedParents.size(); i++) { |
| reachableBy((IDependencyInfo) processedParents.get(i)); |
| } |
| } |
| } |
| |
| public boolean reachableBy(IDependencyInfo info) { |
| if (! (info instanceof UpwardReachableInfo)) { |
| throw new UnsupportedOperationException(); |
| } |
| if (debug) { |
| System.out.println("LD> Entry reachableBy: this -> " + this); //$NON-NLS-1$ |
| System.out.println("LD> Entry reachableBy: info -> " + info); //$NON-NLS-1$ |
| System.out.println(""); //$NON-NLS-1$ |
| } |
| return reachableBy((UpwardReachableInfo) info, new Stack(), new HashMap()); |
| } |
| |
| private boolean reachableBy(UpwardReachableInfo info, Stack stack, Map seen) { |
| stack.push(info); |
| if (debug) { |
| System.out.println("LD> reachableBy: this -> " + this); //$NON-NLS-1$ |
| System.out.println("LD> reachableBy: info -> " + info); //$NON-NLS-1$ |
| System.out.println(""); //$NON-NLS-1$ |
| } |
| boolean ret = reachableBy_(info, stack, seen); |
| stack.pop(); |
| return ret; |
| } |
| |
| private boolean reachableBy_(UpwardReachableInfo info, Stack stack, Map seen) { |
| Map testMap = info.parentMap; |
| if (testMap == null || testMap.isEmpty()) { |
| return false; |
| } |
| |
| if (testMap.containsKey(element.getGuid())) { |
| if (debug) { |
| System.out.println("LD> Contained in parentMap of: " + info); //$NON-NLS-1$ |
| } |
| stack.push(this); |
| mgr.logCircularDependency((Stack) stack.clone()); |
| stack.pop(); |
| return true; |
| } |
| |
| if (seen.containsKey(info.getElement().getGuid())) { |
| return false; |
| } |
| |
| |
| seen.put(info.getElement().getGuid(), info); |
| |
| for (Iterator it = testMap.values().iterator(); it.hasNext();) { |
| UpwardReachableInfo parentInfo = (UpwardReachableInfo) it.next(); |
| if (parentInfo.containedIn(stack)) { |
| if (debug) { |
| System.out.println("LD> containedIn stack: " + info); //$NON-NLS-1$ |
| } |
| stack.push(parentInfo); |
| mgr.logCircularDependency((Stack) stack.clone()); |
| stack.pop(); |
| return true; |
| } |
| if (reachableBy(parentInfo, stack, seen)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public MethodElement getElement() { |
| return element; |
| } |
| |
| private List getMixedParentList() { |
| List list = new ArrayList(); |
| collectParentList(element, list); |
| VariabilityElement ve = (VariabilityElement) element; |
| collectParentListByVariantPaths(ve, list); |
| if (ve.getVariabilityType() == VariabilityType.REPLACES ) { |
| mgr.addToReplacerMap(this); |
| } |
| return list; |
| } |
| |
| private void collectParentList(MethodElement elem, List list) { |
| List parentList = getParentList(elem); |
| |
| int sz = parentList == null ? 0 : parentList.size(); |
| if (mgr.isMoveElement(elem)) { |
| if (sz > 1 && !(elem instanceof CustomCategory || elem instanceof Practice)) { |
| LibraryEditPlugin.getDefault().getLogger().logError("Error in collectParentList"); //$NON-NLS-1$ |
| } |
| } else if (sz > 0) { |
| list.addAll(parentList); |
| } |
| |
| } |
| |
| public static List getParentList(MethodElement elem) { |
| List parentList = null; |
| |
| if (elem instanceof CustomCategory) { |
| parentList = AssociationHelper.getCustomCategories((CustomCategory) elem); |
| parentList = combine(parentList, AssociationHelper.getPractices((CustomCategory) elem)); |
| |
| } else if (elem instanceof Deliverable) { |
| parentList = AssociationHelper.getDeliverables((Deliverable) elem); |
| } else { |
| Object parent = getSameTypeParent(elem); |
| if (parent != null) { |
| parentList = new ArrayList(); |
| parentList.add(parent); |
| } |
| if (elem instanceof Practice) { |
| parentList = combine(parentList, AssociationHelper.getCustomCategories((Practice) elem)); |
| parentList = combine(parentList, AssociationHelper.getPractices((Practice) elem)); |
| } |
| } |
| return parentList; |
| } |
| |
| private static List combine(List list1, List list2) { |
| List ret = new ArrayList(); |
| if (list1 != null) { |
| ret.addAll(list1); |
| } |
| if (list2 != null) { |
| ret.addAll(list2); |
| } |
| return ret; |
| } |
| |
| private void collectParentListByVariantPaths(VariabilityElement ve, List list) { |
| if (! mgr.isFilterElement(ve)) { |
| VariabilityElement parentVe = ve.getVariabilityBasedOnElement(); |
| VariabilityType type = parentVe == null ? null : ve.getVariabilityType(); |
| if (type == VariabilityType.CONTRIBUTES || type == VariabilityType.REPLACES) { |
| list.add(parentVe); |
| } |
| } |
| |
| if (mgr.isDndElement(ve)) { |
| return; |
| } |
| |
| for (Iterator it = TngUtil.getImmediateVarieties(ve, VariabilityType.EXTENDS) ; it.hasNext();) { |
| VariabilityElement extender = (VariabilityElement) it.next(); |
| if (! mgr.isFilterElement(extender)) { |
| list.add(extender); |
| } |
| } |
| |
| } |
| |
| private static MethodElement getSameTypeParent(MethodElement elem) { |
| if (elem instanceof Activity) { |
| return ((Activity) elem).getSuperActivities(); |
| } |
| |
| MethodElement parent = (MethodElement) elem.eContainer(); |
| return parent.getType() == elem.getType() ? parent : null; |
| } |
| |
| public String toString() { |
| return TngUtil.getLabelWithPath(element); |
| } |
| |
| public boolean inheritAncestor(VariabilityType type) { |
| VariabilityElement parentVe = ((VariabilityElement) element).getVariabilityBasedOnElement(); |
| if (parentVe == null) { |
| return false; |
| } |
| if (((VariabilityElement) element).getVariabilityType() != type) { |
| return false; |
| } |
| ArrayList mixParentList = new ArrayList(); |
| collectParentList(element, mixParentList); |
| if (mixParentList != null && mixParentList.contains(parentVe)) { |
| return true; |
| } |
| |
| UpwardReachableInfo parentVeInfo = (UpwardReachableInfo) parentMap.get(parentVe.getGuid()); |
| for (Iterator it = parentMap.values().iterator(); it.hasNext();) { |
| UpwardReachableInfo parentInfo = (UpwardReachableInfo) it.next(); |
| if (parentVeInfo == parentInfo) { |
| continue; |
| } |
| if (parentVeInfo.reachableBy(parentInfo)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| //This would scale up for performance when infoList gets large |
| private boolean containedIn(List infoList) { |
| int sz = infoList == null ? 0 : infoList.size(); |
| if (sz == 0) { |
| return false; |
| } |
| String guid = getElement().getGuid(); |
| if (sz == 1) { |
| return guid.equals(((IDependencyInfo) infoList.get(0)).getElement().getGuid()); |
| } |
| Map listMap = new HashMap(); |
| for (int i=0; i<sz; i++) { |
| IDependencyInfo info = (IDependencyInfo) infoList.get(i); |
| listMap.put(info.getElement().getGuid(), info); |
| } |
| |
| return listMap.containsKey(guid); |
| } |
| |
| } |