blob: 1779b4843a4966f01f7dff984395c39395f16d3a [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.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.MethodPlugin;
import org.eclipse.epf.uma.VariabilityElement;
import org.eclipse.epf.uma.VariabilityType;
import org.eclipse.epf.uma.util.AssociationHelper;
/**
* This class handles plugin dependency info for circular dependency check
*
* @author Weiping Lu
* @since 1.2
*/
public class PluginDepInfo {
private PluginDependencyInfoMgr mgr;
private MethodPlugin plugin;
private HashMap parentMap;
private boolean complete = false;
private boolean debug = false;
protected PluginDepInfo(PluginDependencyInfoMgr mgr, MethodPlugin plugin) {
this.mgr = mgr;
this.plugin = plugin;
}
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(getPlugin());
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 = getPlugin().getBases();
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++) {
MethodPlugin parentElem = (MethodPlugin) parents.get(i);
PluginDepInfo pinfo = (PluginDepInfo) mgr.getProcessedInfo(parentElem);
if (pinfo == null){
pinfo = new PluginDepInfo(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((PluginDepInfo) processedParents.get(i));
}
}
}
public boolean reachableBy(PluginDepInfo info) {
if (! (info instanceof PluginDepInfo)) {
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((PluginDepInfo) info, new Stack(), new HashMap());
}
private boolean reachableBy(PluginDepInfo 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_(PluginDepInfo info, Stack stack, Map seen) {
Map testMap = info.parentMap;
if (testMap == null || testMap.isEmpty()) {
return false;
}
if (testMap.containsKey(getPlugin().getGuid())) {
if (debug) {
System.out.println("LD> Contained in parentMap of: " + info); //$NON-NLS-1$ //$NON-NLS-1$
}
stack.push(this);
mgr.logCircularDependency((Stack) stack.clone());
stack.pop();
return true;
}
if (seen.containsKey(info.getPlugin().getGuid())) {
return false;
}
seen.put(info.getPlugin().getGuid(), info);
for (Iterator it = testMap.values().iterator(); it.hasNext();) {
PluginDepInfo parentInfo = (PluginDepInfo) it.next();
if (parentInfo.containedIn(stack)) {
if (debug) {
System.out.println("LD> containedIn stack: " + info); //$NON-NLS-1$ //$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 MethodPlugin getPlugin() {
return plugin;
}
public String toString() {
return TngUtil.getLabelWithPath(plugin);
}
//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 = getPlugin().getGuid();
if (sz == 1) {
return guid.equals(((PluginDepInfo) infoList.get(0)).getPlugin().getGuid());
}
Map listMap = new HashMap();
for (int i=0; i<sz; i++) {
PluginDepInfo info = (PluginDepInfo) infoList.get(i);
listMap.put(info.getPlugin().getGuid(), info);
}
return listMap.containsKey(guid);
}
}