/***************************************************************************** * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. *****************************************************************************/ package org.eclipse.buckminster.core.metadata.model; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.buckminster.core.RMContext; import org.eclipse.buckminster.core.cspec.QualifiedDependency; import org.eclipse.buckminster.core.cspec.model.CSpec; import org.eclipse.buckminster.core.cspec.model.ComponentIdentifier; import org.eclipse.buckminster.core.cspec.model.ComponentRequest; import org.eclipse.buckminster.core.metadata.parser.ElementRefHandler; import org.eclipse.buckminster.core.mspec.model.MaterializationSpec; import org.eclipse.buckminster.core.query.IAdvisorNode; import org.eclipse.buckminster.core.query.model.ComponentQuery; import org.eclipse.buckminster.core.resolver.NodeQuery; import org.eclipse.buckminster.sax.Utils; import org.eclipse.core.runtime.CoreException; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** * @author Thomas Hallgren */ public class ResolvedNode extends BOMNode { public static final String ATTR_RESOLUTION_ID = "resolutionId"; //$NON-NLS-1$ public static final String CHILD_TAG = "child"; //$NON-NLS-1$ @SuppressWarnings("hiding") public static final String TAG = "resolvedNode"; //$NON-NLS-1$ private final List<BOMNode> children; private final Resolution resolution; /** * This constructor will create a resolved node that has all of its children * unresolved. The set of children is and how the children are qualified are * determined using the attributes and prune flag defined in the * <code>cquery</code>. * * @param cquery * The component query whos advice will be used * @param resolution * The resolution for the created resolved node * @throws CoreException */ public ResolvedNode(NodeQuery query, Resolution resolution) throws CoreException { this.resolution = resolution; CSpec cspec = resolution.getCSpec().prune(query.getContext(), query.getProperties(), query.isPrune(), resolution.getQualifiedDependency().getAttributeNames()); List<QualifiedDependency> qDeps = cspec.getQualifiedDependencies(query.isPrune()); int nDeps = qDeps.size(); if (nDeps == 0) children = Collections.emptyList(); else { ComponentQuery cquery = query.getComponentQuery(); List<BOMNode> childList = new ArrayList<BOMNode>(nDeps); for (QualifiedDependency qDep : qDeps) { ComponentRequest request = qDep.getRequest(); IAdvisorNode override = cquery.getMatchingNode(request, query.getContext()); if (override != null) { qDep = qDep.applyAdvice(override); if (qDep == null) // // We don't want anything at all from this component. // All attributes // were pruned // continue; } UnresolvedNode node = new UnresolvedNode(qDep); childList.add(node); } children = Collections.unmodifiableList(childList); } } public ResolvedNode(Resolution resolution, List<BOMNode> children) { this(resolution, children, false); } public ResolvedNode(Resolution resolution, List<BOMNode> children, boolean forBuild) { this.resolution = resolution; // If forBuild is true, we still wrap the list in an unmodifiableList. // This means // that only the caller of this constructor is able to modify the // content. We // don't call the createUnmodifiableList in that case though, since that // creates // a full copy of the list. // this.children = forBuild ? Collections.unmodifiableList(children) : Utils.createUnmodifiableList(children); } @Override public void addUnresolved(List<ComponentRequest> unresolved, Set<Resolution> skipThese) { if (skipThese.add(getResolution())) { for (BOMNode child : getChildren()) child.addUnresolved(unresolved, skipThese); } } @Override public List<Resolution> findAll(Set<Resolution> skipThese) throws CoreException { HashSet<Resolution> notThese = new HashSet<Resolution>(); if (skipThese != null) notThese.addAll(skipThese); List<Resolution> all = new ArrayList<Resolution>(); collectAll(notThese, all); return all; } @Override public synchronized List<BOMNode> getChildren() { return children; } @Override public String getDefaultTag() { return TAG; } @Override public QualifiedDependency getQualifiedDependency() { return getResolution().getQualifiedDependency(); } @Override public ComponentRequest getRequest() { return getResolution().getRequest(); } @Override public synchronized Resolution getResolution() { return resolution; } @Override public String getViewName() throws CoreException { Resolution r = getResolution(); StringBuilder bld = new StringBuilder(); r.getRequest().appendViewName(bld); bld.append(':'); r.getVersionMatch().toString(bld); return bld.toString(); } /** * Checks if the nodeID is equal to one of the children ids. * * @param nodeID * The nodeID to check for. * @return true if the nodeID was equal to one of the children ids. */ @Override public boolean isChild(BOMNode node) { for (BOMNode child : children) if (child.equals(node)) return true; return false; } @Override public final boolean isReferencing(BOMNode node, boolean shallow) throws CoreException { if (equals(node)) return true; for (BOMNode child : children) if (child.equals(node)) return true; if (!shallow) { for (BOMNode child : children) if (child.isReferencing(node, shallow)) return true; } return false; } @Override protected void addAttributes(AttributesImpl attrs) { Utils.addAttribute(attrs, ATTR_RESOLUTION_ID, resolution.getId().toString()); } @Override protected void emitElements(ContentHandler receiver, String namespace, String prefix) throws SAXException { if (children.size() > 0) { String childName = Utils.makeQualifiedName(prefix, CHILD_TAG); for (BOMNode child : children) { AttributesImpl attrs = new AttributesImpl(); Utils.addAttribute(attrs, ElementRefHandler.ATTR_REFID, child.getId().toString()); receiver.startElement(namespace, CHILD_TAG, childName, attrs); receiver.endElement(namespace, CHILD_TAG, childName); } } } @Override protected boolean isFullyResolved(ComponentQuery query, HashSet<BOMNode> seen, Map<String, ? extends Object> properties) throws CoreException { if (seen.add(this)) { for (BOMNode child : getChildren()) if (!child.isFullyResolved(query, seen, properties)) return false; } return true; } @Override void addMaterializationCandidates(RMContext context, List<Resolution> resolutions, ComponentQuery query, MaterializationSpec mspec, Set<Resolution> perused) throws CoreException { Resolution r = getResolution(); if (perused.add(r)) { for (BOMNode child : getChildren()) child.addMaterializationCandidates(context, resolutions, query, mspec, perused); ComponentIdentifier ci = r.getComponentIdentifier(); if (r.isMaterializable() && !(query.skipComponent(ci, context) || mspec.isExcluded(r))) resolutions.add(r); } } @Override void collectAll(Set<Resolution> notThese, List<Resolution> all) throws CoreException { Resolution r = getResolution(); if (notThese.add(r)) { // It's rather important that we do depth first here and store // the child before its parent since they need to be materialized // and bound in that order. // for (BOMNode child : getChildren()) child.collectAll(notThese, all); all.add(getResolution()); } } @Override BOMNode replaceNode(BOMNode node, Map<BOMNode, BOMNode> visited) throws CoreException { BOMNode self = super.replaceNode(node, visited); if (self != this) return self; List<BOMNode> newChildren = null; List<BOMNode> oldChildren = getChildren(); int numChildren = oldChildren.size(); for (int idx = 0; idx < numChildren; ++idx) { BOMNode oldChild = oldChildren.get(idx); BOMNode newChild = oldChild.replaceNode(node, visited); if (oldChild == newChild) continue; if (newChildren == null) { newChildren = new ArrayList<BOMNode>(numChildren); newChildren.addAll(oldChildren); } newChildren.set(idx, newChild); } if (newChildren == null) return this; self = new ResolvedNode(getResolution(), newChildren); visited.put(this, self); return self; } }