blob: 24827360d7b9ddf8462338fa3f1a0b0804e1ba26 [file] [log] [blame]
//------------------------------------------------------------------------------
// 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);
}
}