blob: c03d699c481742f54abe3086821e5ebdfb11c344 [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.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.epf.common.utils.ProfilingUtil;
import org.eclipse.epf.library.edit.LibraryEditPlugin;
import org.eclipse.epf.library.edit.LibraryEditResources;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.Artifact;
import org.eclipse.epf.uma.CustomCategory;
import org.eclipse.epf.uma.Deliverable;
import org.eclipse.epf.uma.Domain;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodLibrary;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.Practice;
import org.eclipse.epf.uma.VariabilityElement;
import org.eclipse.epf.uma.VariabilityType;
/**
* This class manages circular dependency info.
*
* @author Weiping Lu
* @since 1.2
*/
public class DependencyInfoMgr {
private static boolean profiling = false;
private static boolean debug = false;
private MethodLibrary lib;
private Map reachableInfoMap; //{guid, upwardReachableInfo}
private Map replacerMap;
private Map processed;
private MethodElement filterElement;
private MethodElement moveElement;
private List circularList;
private boolean dndBit = false;
public DependencyInfoMgr(MethodLibrary lib) {
this.lib = lib;
}
public void logCircularDependency(Stack trace) {
if (circularList == null) {
circularList = new ArrayList();
}
circularList.addAll(trace);
}
private List getAndClearCircularList() {
List ret = circularList;
circularList = null;
return ret;
}
public boolean isFilterElement(MethodElement elem) {
return filterElement == elem;
}
public boolean isMoveElement(MethodElement elem) {
return moveElement == elem;
}
private void log(String msg) {
LibraryEditPlugin.INSTANCE.log(msg);
}
public IStatus checkCircularDependnecy(VariabilityElement ve) {
if (debug) {
System.out.println("LD> ve: " + TngUtil.getLabelWithPath(ve)); //$NON-NLS-1$
}
IDependencyInfo info = registerVariabilityElement(ve, false, false, true);
List cirList = getAndClearCircularList();
boolean loop = cirList != null && !cirList.isEmpty();
boolean replacingAncestor = info.inheritAncestor(VariabilityType.REPLACES);
boolean actInherentingAncestor = getActInerentingAncestor(ve);
MultiStatus multiStatus = new MultiStatus(LibraryEditPlugin.PLUGIN_ID, 0, "", null); //$NON-NLS-1$
if (loop) {
log("Error> Circular dependency detected: "); //$NON-NLS-1$
for (int i=0; i<cirList.size(); i++) {
IDependencyInfo loopInfo = (IDependencyInfo) cirList.get(i);
MethodElement elem = loopInfo.getElement();
log("Error> " + i + ": " + TngUtil.getLabelWithPath(elem));//$NON-NLS-1$ //$NON-NLS-2$
}
multiStatus.add(new ValidationStatus(IStatus.ERROR, 0, genErrorMsg(cirList), ve, null));
}
if (replacingAncestor) {
MethodElement elem = info.getElement();
log("Error> " + TngUtil.getLabelWithPath(elem) + " is replacing an ancestor.");//$NON-NLS-1$ //$NON-NLS-2$
multiStatus.add(new ValidationStatus(IStatus.ERROR, 0, LibraryEditResources.replacing_ancestor_error_msg, ve, null));
}
if (actInherentingAncestor) {
MethodElement elem = info.getElement();
multiStatus.add(new ValidationStatus(IStatus.ERROR, 0, LibraryEditResources.circular_dependency_error_msg, ve, null));
}
return multiStatus;
}
private boolean getActInerentingAncestor(VariabilityElement ve) {
if (ve instanceof Activity) {
Activity base = (Activity) ve.getVariabilityBasedOnElement();
if (base != null) {
for (Activity act = ((Activity) ve).getSuperActivities();
act != null; act = act.getSuperActivities()) {
if (act == base) {
return true;
}
}
}
}
return false;
}
private String genErrorMsg(List cirList) {
String msg = LibraryEditResources.variability_element_circular_loop_error_msg;
for (int i=0; i<cirList.size(); i++) {
IDependencyInfo loopInfo = (IDependencyInfo) cirList.get(i);
MethodElement elem = loopInfo.getElement();
if (i > 0) {
msg += ": "; //$NON-NLS-1$
}
msg += TngUtil.getLabelWithPath(elem);
}
return msg;
}
public IDependencyInfo registerVariabilityElement(VariabilityElement ve,
boolean isFilterElement, boolean isMoveElement) {
return registerVariabilityElement(ve, isFilterElement, isMoveElement, false);
}
private IDependencyInfo registerVariabilityElement(VariabilityElement ve,
boolean isFilterElement, boolean isMoveElement, boolean checkCircular) {
if (isFilterElement) {
filterElement = ve;
}
if (isMoveElement) {
moveElement = ve;
}
IDependencyInfo info = getProcessedInfo(ve);
if (info == null) {
info = newDependencyInfo(this, ve);
}
if (! info.isComplete()) {
info.build(checkCircular);
}
return info;
}
protected boolean processed(MethodElement element) {
return processed != null && processed.containsKey(element.getGuid());
}
protected IDependencyInfo getProcessedInfo(MethodElement element) {
return processed == null ? null : (IDependencyInfo) processed.get(element.getGuid());
}
protected void addToProcessed(IDependencyInfo info) {
if (processed == null) {
processed = new HashMap();
}
processed.put(info.getElement().getGuid(), info);
}
protected void addToReplacerMap(IDependencyInfo replacerInfo) {
if (replacerMap == null) {
replacerMap = new HashMap();
}
replacerMap.put(replacerInfo.getElement().getGuid(), replacerInfo);
}
private IDependencyInfo addToReachableInfoMap(MethodElement elem) {
if (reachableInfoMap == null) {
reachableInfoMap = new HashMap();
}
IDependencyInfo info = (IDependencyInfo) reachableInfoMap.get(elem.getGuid());
if (info == null) {
info = newDependencyInfo(this, elem);
reachableInfoMap.put(elem.getGuid(), info);
}
return info;
}
/*
* Detect circular dependency for the whole library
*/
public boolean hasCircularDependency() {
buildReachableInfoMap();
boolean replacingAcestor = false;
if (replacerMap != null) {
for (Iterator it = replacerMap.values().iterator(); it.hasNext();) {
UpwardReachableInfo info = (UpwardReachableInfo) it.next();
if (info.inheritAncestor(VariabilityType.REPLACES)) {
replacingAcestor = true;
}
}
}
if (debug && circularList != null) {
System.out.println("LD> Circular loops detected: "); //$NON-NLS-1$
for (Iterator it = circularList.iterator(); it.hasNext();) {
Stack stack = (Stack) it.next();
for (int i=0; i<stack.size(); i++) {
System.out.println("LD> " + (i) + ": " + stack.get(i)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
return replacingAcestor || numCircularDependency() > 0;
}
private int numCircularDependency() {
return circularList == null ? 0 : circularList.size();
}
//to be specialized by sub classes
protected IDependencyInfo newDependencyInfo(DependencyInfoMgr mgr, MethodElement elem) {
return new UpwardReachableInfo(this, elem);
}
private void buildReachableInfoMap() {
long usedMem0 = 0;
Runtime rt = Runtime.getRuntime();
if (profiling) {
for (Iterator it = lib.eAllContents(); it.hasNext();) {
it.next();
}
ProfilingUtil.fullGC();
usedMem0 = rt.totalMemory() - rt.freeMemory();
}
buildReachableInfoMap_();
if (profiling) {
ProfilingUtil.fullGC();
System.out.println("LD> usedMem0: " + usedMem0/1000 + " k bytes"); //$NON-NLS-1$ //$NON-NLS-2$
long usedMem1 = rt.totalMemory() - rt.freeMemory();
System.out.println("LD> usedMem1: " + usedMem1/1000 + " k bytes"); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println("LD> diffMem: " + (usedMem1 - usedMem0)/1000 + " k bytes"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void buildReachableInfoMap_() {
reachableInfoMap = new HashMap();
List plugins = lib.getMethodPlugins();
for (int i = 0; i < plugins.size(); i++) {
MethodPlugin plugin = (MethodPlugin) plugins.get(i);
for (Iterator it = plugin.eAllContents(); it.hasNext();) {
Object obj = it.next();
if (VeToCheck(obj)) {
addToReachableInfoMap((MethodElement) obj);
}
}
}
for (Iterator it = reachableInfoMap.values().iterator(); it.hasNext();) {
IDependencyInfo info = (IDependencyInfo) it.next();
if (! processed(info.getElement())) {
info.build(true);
}
}
}
public static boolean VeToCheck(Object obj) {
return obj instanceof Domain ||
obj instanceof CustomCategory ||
obj instanceof Artifact ||
obj instanceof Practice ||
obj instanceof Deliverable ||
obj instanceof Activity;
}
/*
* Check circular dependency for the library.
*/
public CheckResult checkCircularDependnecy(Tracer tracer) {
long usedMem0 = 0;
Runtime rt = Runtime.getRuntime();
if (profiling) {
for (Iterator it = lib.eAllContents(); it.hasNext();) {
it.next();
}
ProfilingUtil.fullGC();
usedMem0 = rt.totalMemory() - rt.freeMemory();
}
CheckResult result = checkCircularDependnecy_(tracer);
if (profiling) {
ProfilingUtil.fullGC();
System.out.println("LD> usedMem0: " + usedMem0/1000 + " k bytes"); //$NON-NLS-1$ //$NON-NLS-2$
long usedMem1 = rt.totalMemory() - rt.freeMemory();
System.out.println("LD> usedMem1: " + usedMem1/1000 + " k bytes"); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println("LD> diffMem: " + (usedMem1 - usedMem0)/1000 + " k bytes"); //$NON-NLS-1$ //$NON-NLS-2$
}
return result;
}
private CheckResult checkCircularDependnecy_(Tracer tracer) {
CheckResult result = new CheckResult();
HashSet seen = new HashSet();
List plugins = lib.getMethodPlugins();
for (Iterator it = plugins.iterator(); it.hasNext();) {
MethodPlugin plugin = (MethodPlugin) it.next();
for (Iterator iit = plugin.eAllContents(); iit.hasNext();) {
Object obj = iit.next();
if (seen.contains(obj)) {
continue;
}
seen.add(obj);
if (VeToCheck(obj)) {
IDependencyInfo info = registerVariabilityElement((VariabilityElement) obj, false, false, true);
List cirList = getAndClearCircularList();
boolean loop = cirList != null && !cirList.isEmpty();
boolean replacingAncestor = info.inheritAncestor(VariabilityType.REPLACES);
if (loop) {
tracer.trace("Error> Circular dependency detected: "); //$NON-NLS-1$
for (int i=0; i<cirList.size(); i++) {
IDependencyInfo loopInfo = (IDependencyInfo) cirList.get(i);
MethodElement elem = loopInfo.getElement();
tracer.trace("Error> " + i + ": " + TngUtil.getLabelWithPath(elem));//$NON-NLS-1$ //$NON-NLS-2$
}
result.circularList.add(cirList);
}
if (replacingAncestor) {
MethodElement elem = info.getElement();
tracer.trace("Error> " + TngUtil.getLabelWithPath(elem) + " is replacing an ancestor.");//$NON-NLS-1$ //$NON-NLS-2$
result.replacingAncestorList.add(info);
}
}
}
}
return result;
}
public static class CheckResult {
public List circularList = new ArrayList();
public List replacingAncestorList = new ArrayList();
public int getErrorCount() {
return circularList.size() + replacingAncestorList.size();
}
public int getCircularElementCount() {
int count = replacingAncestorList.size();
for (int i=0; i<circularList.size(); i++) {
List list = (List) circularList.get(i);
int sz = list == null ? 0 : list.size();
count += sz;
}
return count;
}
}
protected boolean isDndElement(MethodElement elem) {
return dndBit && moveElement == elem;
}
protected void setDndBit(boolean b) {
dndBit = b;
}
}