/*******************************************************************************
* Copyright (c) 2004, 2006
* Thomas Hallgren, Kenneth Olwing, Mitch Sonies
* Pontus Rydin, Nils Unden, Peer Torngren
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the individual
* copyright holders listed above, as Initial Contributors under such license.
* The text of such license is available at www.eclipse.org.
*******************************************************************************/
package org.eclipse.buckminster.core.resolver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.UUID;
import org.eclipse.buckminster.core.cspec.IAttribute;
import org.eclipse.buckminster.core.cspec.IPrerequisite;
import org.eclipse.buckminster.core.cspec.QualifiedDependency;
import org.eclipse.buckminster.core.cspec.builder.CSpecBuilder;
import org.eclipse.buckminster.core.cspec.builder.TopLevelAttributeBuilder;
import org.eclipse.buckminster.core.cspec.model.CSpec;
import org.eclipse.buckminster.core.cspec.model.CircularDependencyException;
import org.eclipse.buckminster.core.cspec.model.Generator;
import org.eclipse.buckminster.core.ctype.IComponentType;
import org.eclipse.buckminster.core.metadata.model.BOMNode;
import org.eclipse.buckminster.core.metadata.model.BillOfMaterials;
import org.eclipse.buckminster.core.metadata.model.GeneratorNode;
import org.eclipse.buckminster.core.metadata.model.Resolution;
import org.eclipse.buckminster.core.metadata.model.ResolvedNode;
import org.eclipse.buckminster.core.metadata.model.UnresolvedNode;
import org.eclipse.buckminster.core.query.model.ComponentQuery;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.equinox.p2.metadata.VersionRange;
public class ResolverNode {
private static final ResolverNode[] noChildren = new ResolverNode[0];
private ResolverNode[] children;
private GeneratorNode generatorNode;
private boolean invalidateRun;
private NodeQuery query;
private Resolution resolution;
private boolean forceUnresolved;
private final String tagInfo;
public ResolverNode(NodeQuery query, String tagInfo) {
this.query = query;
this.children = noChildren;
this.tagInfo = tagInfo;
if (tagInfo != null)
query.getContext().addTagInfo(query.getComponentRequest(), tagInfo);
}
public synchronized void addDependencyQualification(QualifiedDependency newQDep, String tagInf) throws CoreException {
NodeQuery qualifiedQuery = query.addDependencyQualification(newQDep);
if (qualifiedQuery == query)
//
// Old query already declared the needed purposes.
//
return;
VersionRange newVd = qualifiedQuery.getVersionRange();
if (resolution != null) {
// Re-resolve might be necessary
//
if ((newVd == null || newVd.isIncluded(resolution.getVersion()))
&& query.getQualifiedDependency().hasAllAttributes(qualifiedQuery.getRequiredAttributes())) {
// Nope, the resolution is still valid for this new query
//
query = qualifiedQuery;
if (tagInfo != null)
qualifiedQuery.getContext().addTagInfo(qualifiedQuery.getComponentRequest(), tagInf);
return;
}
}
// New version constraints or new attributes were introduced that
// invalidated the
// current resolution. We need to invalidate what we have and make sure
// its done
// again.
//
resolution = null;
children = noChildren;
query = qualifiedQuery;
invalidateRun = true;
if (tagInfo != null)
qualifiedQuery.getContext().addTagInfo(qualifiedQuery.getComponentRequest(), tagInf);
}
public BOMNode collectNodes(Map<UUID, BOMNode> nodeMap, Stack<Resolution> circularDepTrap, boolean sameTop) throws CoreException {
if (query.skipComponent())
return null;
if (generatorNode != null)
return generatorNode;
if (resolution == null)
return new UnresolvedNode(query.getQualifiedDependency());
UUID myID = resolution.getId();
BOMNode node = nodeMap.get(myID);
if (node != null)
return node;
if (circularDepTrap.contains(resolution)) {
if (query.allowCircularDependency())
return null;
ArrayList<String> attrs = new ArrayList<String>(circularDepTrap.size());
for (Resolution res : circularDepTrap)
attrs.add(res.getCSpec().getName());
attrs.add(resolution.getName());
throw new CircularDependencyException(attrs);
}
boolean transitive = true;
if (IComponentType.OSGI_BUNDLE.equals(resolution.getComponentTypeId())) {
// We don't traverse the children of source bundles since that
// dependency is synthesized
transitive = !resolution.getName().endsWith(".source"); //$NON-NLS-1$
}
List<BOMNode> childNodes;
int top = children.length;
ComponentQuery cquery = query.getComponentQuery();
if (transitive && top > 0) {
try {
ArrayList<BOMNode> childNodeArr = new ArrayList<BOMNode>(top);
circularDepTrap.push(resolution);
for (ResolverNode child : children) {
boolean sameChildTop = cquery.equals(child.query.getComponentQuery());
BOMNode childNode = child.collectNodes(nodeMap, circularDepTrap, sameChildTop);
if (childNode == null) {
// We encountered a skipped component or an allowed
// circular dependency. This
// means we must alter the resolution of this node
//
String depName = child.getQuery().getComponentRequest().getName();
CSpec cspec = resolution.getCSpec();
CSpecBuilder bld = new CSpecBuilder();
bld.initFrom(cspec);
for (IAttribute attr : cspec.getAttributes().values()) {
for (IPrerequisite pq : attr.getPrerequisites()) {
if (depName.equals(pq.getComponentName()))
((TopLevelAttributeBuilder) bld.getAttribute(attr.getName())).removePrerequisite(pq);
}
}
bld.removeDependency(depName);
cspec = bld.createCSpec();
resolution = new Resolution(cspec, resolution);
} else
childNodeArr.add(childNode);
}
circularDepTrap.pop();
childNodes = childNodeArr;
} catch (CircularDependencyException e) {
if (query.allowCircularDependency())
return null;
throw e;
}
} else
childNodes = Collections.emptyList();
node = new ResolvedNode(resolution, childNodes);
if (!sameTop)
node = BillOfMaterials.create(node, cquery);
nodeMap.put(myID, node);
return node;
}
public synchronized void forceUnresolved() {
resolution = null;
children = noChildren;
invalidateRun = true;
forceUnresolved = true;
}
public boolean isForceUnresolved() {
return forceUnresolved;
}
public boolean isResolved() {
return resolution != null;
}
public synchronized void setResolution(Resolution resolution, ResolverNode[] children) {
if (!invalidateRun) {
this.resolution = resolution;
this.children = (children == null) ? noChildren : children;
}
}
public synchronized ResolutionContext startResolvingChildren(BOMNode node) throws CoreException {
Resolution nodeRes = node.getResolution();
if (invalidateRun || nodeRes == null)
return null;
ComponentQuery cquery = node.getQuery();
ResolutionContext originalContext = query.getResolutionContext();
ResolutionContext context = originalContext;
if (!(cquery == null || cquery.equals(context.getComponentQuery())))
context = new ResolutionContext(cquery, context);
CSpec cspec = nodeRes.getCSpec();
Collection<Generator> generators = cspec.getGeneratorList();
if (generators.size() > 0) {
if (context == originalContext)
context = new ResolutionContext(originalContext.getComponentQuery(), originalContext);
context.setGenerators(cspec, generators);
}
if (context != originalContext)
query = context.getNodeQuery(query.getQualifiedDependency());
return context;
}
synchronized void clearInvalidationFlag() {
if (!forceUnresolved)
invalidateRun = false;
}
NodeQuery getQuery() {
return query;
}
String getTagInfo() {
return tagInfo;
}
boolean isInvalidated() {
return invalidateRun;
}
void setGeneratorNode(GeneratorNode generatorNode) {
this.generatorNode = generatorNode;
}
}