/*****************************************************************************
* 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.cspec.model;
import static org.eclipse.buckminster.core.XMLConstants.BM_CSPEC_NS;
import static org.eclipse.buckminster.core.XMLConstants.BM_CSPEC_PREFIX;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.UUID;
import org.eclipse.buckminster.core.Messages;
import org.eclipse.buckminster.core.RMContext;
import org.eclipse.buckminster.core.XMLConstants;
import org.eclipse.buckminster.core.common.model.Documentation;
import org.eclipse.buckminster.core.cspec.IAction;
import org.eclipse.buckminster.core.cspec.IActionArtifact;
import org.eclipse.buckminster.core.cspec.IAttribute;
import org.eclipse.buckminster.core.cspec.IAttributeFilter;
import org.eclipse.buckminster.core.cspec.ICSpecData;
import org.eclipse.buckminster.core.cspec.IGenerator;
import org.eclipse.buckminster.core.cspec.IGroup;
import org.eclipse.buckminster.core.cspec.IPrerequisite;
import org.eclipse.buckminster.core.cspec.PathGroup;
import org.eclipse.buckminster.core.cspec.QualifiedDependency;
import org.eclipse.buckminster.core.cspec.SaxablePath;
import org.eclipse.buckminster.core.cspec.WellknownActions;
import org.eclipse.buckminster.core.cspec.builder.AttributeBuilder;
import org.eclipse.buckminster.core.cspec.builder.CSpecBuilder;
import org.eclipse.buckminster.core.cspec.builder.ComponentRequestBuilder;
import org.eclipse.buckminster.core.cspec.builder.GeneratorBuilder;
import org.eclipse.buckminster.core.metadata.MissingComponentException;
import org.eclipse.buckminster.core.metadata.ModelCache;
import org.eclipse.buckminster.core.metadata.ReferentialIntegrityException;
import org.eclipse.buckminster.core.metadata.StorageManager;
import org.eclipse.buckminster.core.metadata.WorkspaceInfo;
import org.eclipse.buckminster.core.metadata.model.IModelCache;
import org.eclipse.buckminster.core.metadata.model.IUUIDPersisted;
import org.eclipse.buckminster.osgi.filter.Filter;
import org.eclipse.buckminster.runtime.Trivial;
import org.eclipse.buckminster.sax.UUIDKeyed;
import org.eclipse.buckminster.sax.Utils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionRange;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* @author Thomas Hallgren
*/
public class CSpec extends UUIDKeyed implements IUUIDPersisted, ICSpecData {
public static Set<IPath> createUnmodifiablePaths(Set<IPath> aSet) {
if (aSet == null || aSet.size() == 0)
aSet = Collections.emptySet();
else {
HashSet<IPath> saxablePaths = new HashSet<IPath>();
for (IPath path : aSet)
saxablePaths.add(SaxablePath.coerce(path));
aSet = Collections.unmodifiableSet(saxablePaths);
}
return aSet;
}
public static String getTagInfo(ComponentIdentifier ci, URL projectInfoURL, String parentInfo) {
StringBuilder bld = new StringBuilder();
if (projectInfoURL != null) {
bld.append("project: "); //$NON-NLS-1$
bld.append(projectInfoURL);
bld.append(", "); //$NON-NLS-1$
}
if (parentInfo != null) {
int pathIdx = parentInfo.indexOf("path: "); //$NON-NLS-1$
if (projectInfoURL == null)
bld.append(parentInfo);
else if (pathIdx >= 0)
bld.append(parentInfo, pathIdx, parentInfo.length());
if (pathIdx >= 0)
bld.append(" -> "); //$NON-NLS-1$
else
bld.append(", path: "); //$NON-NLS-1$
} else
bld.append("path: "); //$NON-NLS-1$
ci.toString(bld);
return bld.toString();
}
public static final String ATTR_FILTER = "filter"; //$NON-NLS-1$
public static final String ATTR_PROJECT_INFO = "projectInfo"; //$NON-NLS-1$
public static final String ATTR_SHORT_DESC = "shortDesc"; //$NON-NLS-1$
public static final String ELEM_ACTIONS = "actions"; //$NON-NLS-1$
public static final String ELEM_ARTIFACTS = "artifacts"; //$NON-NLS-1$
public static final String ELEM_DEPENDENCIES = "dependencies"; //$NON-NLS-1$
public static final String ELEM_GENERATORS = "generators"; //$NON-NLS-1$
public static final String ELEM_DEPENDENCY = "dependency"; //$NON-NLS-1$
public static final String ELEM_GROUPS = "groups"; //$NON-NLS-1$
public static final String SELF_ARTIFACT = "buckminster.component.self"; //$NON-NLS-1$
public static final String TAG = "cspec"; //$NON-NLS-1$
// This string should be something that is very unlikely to appear in a
// component name
public static final String COMPONENT_NAME_TYPE_SEPARATOR = "/!@@!/"; //$NON-NLS-1$
public static final int SEQUENCE_NUMBER = 4;
private static final Comparator<Attribute> attributeSorter = new Comparator<Attribute>() {
@Override
public int compare(Attribute o1, Attribute o2) {
if (o1.isPublic() == o2.isPublic())
return Trivial.compareAllowNull(o1.getName(), o2.getName());
return o1.isPublic() ? -1 : 1;
}
};
private final Map<String, Attribute> attributes;
private final ComponentIdentifier componentIdentifier;
private final List<ComponentRequest> dependencies;
private final Map<ComponentIdentifier, Generator> generators;
private final Documentation documentation;
private final String shortDesc;
private final Attribute selfAttribute;
private final Filter filter;
private final URL projectInfo;
public CSpec(CSpecBuilder cspecBld) {
cspecBld.finalWrapUp();
componentIdentifier = cspecBld.getComponentIdentifier();
projectInfo = cspecBld.getProjectInfo();
documentation = cspecBld.getDocumentation();
shortDesc = cspecBld.getShortDesc();
filter = cspecBld.getFilter();
selfAttribute = new TopLevelAttribute(SELF_ARTIFACT) {
@Override
protected AttributeBuilder createAttributeBuilder(CSpecBuilder cspecBuilder) {
// This should be OK. Noone should ever clone the self attribute
//
return null;
}
@Override
protected PathGroup[] internalGetPathGroups(IModelCache ctx, Map<String, ? extends Object> local, Stack<IAttributeFilter> filters)
throws CoreException {
IPath me = getComponentLocation();
PathGroup meGroup;
if (me.hasTrailingSeparator())
//
// A folder will act as the base for the component
//
meGroup = new PathGroup(me, Trivial.EMPTY_PATH_ARRAY);
else
// The parent folder will be the base since the component
// itself
// is a file.
//
meGroup = new PathGroup(me.removeLastSegments(1).addTrailingSeparator(), new IPath[] { new Path(me.lastSegment()) });
return new PathGroup[] { meGroup };
}
};
selfAttribute.setCSPec(this);
Map<String, AttributeBuilder> attrs = cspecBld.getAttributes();
int top = (attrs == null) ? 0 : attrs.size();
if (top == 0)
attributes = Collections.emptyMap();
else {
Map<String, Attribute> map;
Collection<AttributeBuilder> values = attrs.values();
if (top == 1) {
Attribute attr = values.iterator().next().createAttribute();
attr.setCSPec(this);
map = Collections.singletonMap(attr.getName(), attr);
} else {
map = new HashMap<String, Attribute>(top);
for (AttributeBuilder bld : values) {
Attribute attr = bld.createAttribute();
attr.setCSPec(this);
map.put(attr.getName(), attr);
}
}
attributes = Collections.unmodifiableMap(map);
}
List<ComponentRequestBuilder> deps = cspecBld.getDependencyBuilders();
top = (deps == null) ? 0 : deps.size();
if (top == 0)
dependencies = Collections.emptyList();
else {
List<ComponentRequest> list;
if (top == 1) {
dependencies = Collections.singletonList(deps.get(0).createComponentRequest());
} else {
// We use a TreeMap to assert that the dependencies will be
// written in the exact same order at all times
//
list = new ArrayList<ComponentRequest>();
for (ComponentRequestBuilder dep : deps)
list.add(dep.createComponentRequest());
dependencies = Collections.unmodifiableList(list);
}
}
Collection<GeneratorBuilder> gens = cspecBld.getGeneratorList();
top = gens.size();
if (top == 0)
generators = Collections.emptyMap();
else {
Map<ComponentIdentifier, Generator> map;
if (top == 1) {
GeneratorBuilder bld = gens.iterator().next();
Generator gen = bld.createGenerator(this);
map = Collections.<ComponentIdentifier, Generator> singletonMap(bld.getGeneratedIdentifier(), gen);
} else {
// We use a TreeMap to assert that the generators will be
// written in the exact same order at all times
//
map = new TreeMap<ComponentIdentifier, Generator>();
for (GeneratorBuilder bld : gens) {
Generator gen = bld.createGenerator(this);
map.put(bld.getGeneratedIdentifier(), gen);
}
}
generators = Collections.unmodifiableMap(map);
}
}
@Override
protected void addAttributes(AttributesImpl attrs) {
Utils.addAttribute(attrs, NamedElement.ATTR_NAME, componentIdentifier.getName());
String ctypeID = componentIdentifier.getComponentTypeID();
if (ctypeID != null)
Utils.addAttribute(attrs, ComponentName.ATTR_COMPONENT_TYPE, ctypeID);
Version version = componentIdentifier.getVersion();
if (version != null)
Utils.addAttribute(attrs, ComponentIdentifier.ATTR_VERSION, version.toString());
if (projectInfo != null)
Utils.addAttribute(attrs, ATTR_PROJECT_INFO, projectInfo.toString());
if (shortDesc != null)
Utils.addAttribute(attrs, ATTR_SHORT_DESC, shortDesc);
if (filter != null)
Utils.addAttribute(attrs, ATTR_FILTER, filter.toString());
}
private void addDependencyBundle(Map<ComponentRequest, Set<String>> deps, IAttribute dp) throws CoreException {
// Make sure that the pruned CSpec has all prerequisites
//
for (IPrerequisite prereq : dp.getPrerequisites()) {
if (prereq.isExternal()) {
ComponentRequest rq = getDependency(prereq.getComponentName(), prereq.getComponentType(), prereq.getVersionRange());
Set<String> attrs = deps.get(rq);
if (attrs == null) {
attrs = new HashSet<String>();
deps.put(rq, attrs);
}
attrs.add(prereq.getAttribute());
} else {
IAttribute localGroup = getAttribute(prereq.getAttribute());
if (localGroup == null)
continue;
if (localGroup instanceof IActionArtifact)
localGroup = ((ActionArtifact) localGroup).getAction();
addDependencyBundle(deps, localGroup);
}
}
}
private void addReferencedDependencies(Set<ComponentRequest> deps, Set<String> attrNames, Attribute attr, Stack<IAttributeFilter> filters)
throws CoreException {
if (attrNames.contains(attr.getName()))
return;
// Make sure that the pruned CSpec has all prerequisites
//
attrNames.add(attr.getName());
if (attr instanceof IActionArtifact) {
addReferencedDependencies(deps, attrNames, ((ActionArtifact) attr).getAction(), filters);
return;
}
for (Prerequisite prereq : attr.getPrerequisites(filters)) {
if (prereq.isExternal()) {
if (deps != null)
deps.add(getDependency(prereq.getComponentName(), prereq.getComponentType(), prereq.getVersionRange()));
} else {
Attribute localAttr = getRequiredAttribute(prereq.getAttribute());
if (prereq.isPatternFilter()) {
if (filters == null)
filters = new Stack<IAttributeFilter>();
filters.push(prereq);
addReferencedDependencies(deps, attrNames, localAttr, filters);
filters.pop();
} else
addReferencedDependencies(deps, attrNames, localAttr, filters);
}
}
}
private boolean dependenciesFulfilled(Attribute attr, CSpecBuilder bld, Stack<IAttributeFilter> filters) throws CoreException {
if (attr instanceof IActionArtifact)
attr = ((ActionArtifact) attr).getAction();
for (Prerequisite pq : attr.getPrerequisites(filters)) {
if (pq.isExternal()) {
if (bld.getDependency(pq.getComponentName(), pq.getComponentType(), pq.getVersionRange()) == null)
return false;
continue;
}
Attribute pqAttr;
try {
pqAttr = getRequiredAttribute(pq.getAttribute());
} catch (MissingAttributeException e) {
// Attribute stems from artifact that isn't present during
// simple resolution. We consider this as OK here.
continue;
}
if (pq.isPatternFilter()) {
if (filters == null)
filters = new Stack<IAttributeFilter>();
filters.push(pq);
}
boolean depsFulfilled = dependenciesFulfilled(pqAttr, bld, filters);
if (pq.isPatternFilter())
filters.pop();
if (!depsFulfilled)
return false;
}
return true;
}
@Override
protected void emitElements(ContentHandler handler, String namespace, String prefix) throws SAXException {
if (documentation != null)
documentation.toSax(handler, namespace, prefix, documentation.getDefaultTag());
Utils.emitCollection(namespace, prefix, ELEM_DEPENDENCIES, ELEM_DEPENDENCY, dependencies, handler);
Utils.emitCollection(namespace, prefix, ELEM_GENERATORS, Generator.TAG, generators.values(), handler);
ArrayList<Attribute> topArtifacts = new ArrayList<Attribute>();
ArrayList<Attribute> actions = new ArrayList<Attribute>();
ArrayList<Attribute> groups = new ArrayList<Attribute>();
for (Attribute attr : attributes.values()) {
if (attr instanceof IAction)
actions.add(attr);
else if (attr instanceof IGroup)
groups.add(attr);
else if (!(attr instanceof IActionArtifact))
topArtifacts.add(attr);
}
Collections.sort(topArtifacts, attributeSorter);
Collections.sort(actions, attributeSorter);
Collections.sort(groups, attributeSorter);
Utils.emitCollection(namespace, prefix, ELEM_ARTIFACTS, null, topArtifacts, handler);
Utils.emitCollection(namespace, prefix, ELEM_ACTIONS, null, actions, handler);
Utils.emitCollection(namespace, prefix, ELEM_GROUPS, null, groups, handler);
}
List<ActionArtifact> getActionArtifacts(Action action) {
List<ActionArtifact> artifacts = null;
String actionName = action.getName();
for (IAttribute ag : attributes.values()) {
if (!(ag instanceof IActionArtifact))
continue;
ActionArtifact aa = (ActionArtifact) ag;
if (aa.getActionName().equals(actionName)) {
if (artifacts == null)
artifacts = new ArrayList<ActionArtifact>();
artifacts.add(aa);
}
}
if (artifacts == null)
artifacts = Collections.emptyList();
return artifacts;
}
@Override
public Attribute getAttribute(String name) {
Attribute attr = attributes.get(name);
if (attr == null && name.equals(SELF_ARTIFACT))
attr = selfAttribute;
return attr;
}
@Override
public Map<String, Attribute> getAttributes() {
return attributes;
}
public Attribute[] getAttributes(Collection<String> attributeNames) throws MissingAttributeException {
int sz = attributeNames == null ? 0 : attributeNames.size();
Attribute[] attrs = new Attribute[sz];
int idx = 0;
for (String str : attributeNames)
attrs[idx++] = getRequiredAttribute(str);
return attrs;
}
public Attribute[] getAttributes(String... attributeNames) throws MissingAttributeException {
return getAttributes(Arrays.asList(attributeNames));
}
public List<Attribute> getAttributesProducedByActions(boolean includePrivate) throws CoreException {
ModelCache ctx = new ModelCache();
ArrayList<Attribute> attrs = new ArrayList<Attribute>();
for (Attribute ag : attributes.values())
if ((includePrivate || ag.isPublic()) && ag.isProducedByActions(ctx))
attrs.add(ag);
return attrs;
}
public Attribute getBindEntryPoint() {
return getAttribute(WellknownActions.BUCKMINSTER.BIND_ENTRYPOINT.toString());
}
@Override
public ComponentIdentifier getComponentIdentifier() {
return componentIdentifier;
}
public IPath getComponentLocation() throws CoreException {
return WorkspaceInfo.getComponentLocation(this);
}
@Override
public String getComponentTypeID() {
return componentIdentifier.getComponentTypeID();
}
@Override
public String getDefaultTag() {
return TAG;
}
@Override
public Collection<ComponentRequest> getDependencies() {
return dependencies;
}
@SuppressWarnings("deprecation")
@Deprecated
@Override
public ComponentRequest getDependency(String depName, String depType) throws MissingDependencyException {
return getDependency(depName, depType, null);
}
@Override
public ComponentRequest getDependency(String depName, String depType, VersionRange depRange) throws MissingDependencyException {
int idx = dependencies.size();
while (--idx >= 0) {
ComponentRequest dependency = dependencies.get(idx);
if (!depName.equals(dependency.getName()))
continue;
if (depType != null && dependency.getComponentTypeID() != null && !depType.equals(dependency.getComponentTypeID()))
continue;
if (depRange != null && dependency.getVersionRange() != null && dependency.getVersionRange().intersect(depRange) == null)
continue;
return dependency;
}
throw new MissingDependencyException(componentIdentifier.toString(), depName);
}
@Override
public Documentation getDocumentation() {
return documentation;
}
@Override
protected String getElementNamespace(String namespace) {
return XMLConstants.BM_CSPEC_NS;
}
@Override
protected String getElementPrefix(String prefix) {
return XMLConstants.BM_CSPEC_PREFIX;
}
@Override
public Filter getFilter() {
return filter;
}
@Override
public Collection<Generator> getGeneratorList() {
return generators.values();
}
@Override
public String getName() {
return componentIdentifier.getName();
}
public Attribute getPostbind() {
return getAttribute(WellknownActions.BUCKMINSTER.POSTBIND.toString());
}
public Attribute getPrebind() {
return getAttribute(WellknownActions.BUCKMINSTER.PREBIND.toString());
}
@Override
public URL getProjectInfo() {
return projectInfo;
}
/**
* <p>
* Creates a list of attribute qualified external dependencies that will be
* unique with respect to their respective {@link ComponentRequest}.
* </p>
* <p>
* Only those dependencies that are implied through <code>targets</code> are
* included in the result if the argument <code>prune</code> is set to
* <code>true</code>. When the <code>prune</code> argument is
* <code>false</code>, all external dependencies will be included and those
* not implied by a matched public artifact group will have an empty
* attribute array.
* </p>
*
* @param attributes
* The attributes that implies and qualifies dependencies. Might
* be an empty array but never <code>null</code>.
* @param prune
* True if the result should be pruned to only include
* dependencies that has an attribute.
* @return A list of attribute qualified dependencies, unique per
* IComponentRequest.
* @see #getActionsByName(String[])
* @see #getGroupsByName(String[])
*/
public List<QualifiedDependency> getQualifiedDependencies(boolean attributePrune) throws CoreException {
Map<ComponentRequest, Set<String>> deps = new HashMap<ComponentRequest, Set<String>>();
if (!attributePrune) {
// All dependencies must be included in the result. Even the one for
// which there is no requested attribute.
//
for (ComponentRequest dep : getDependencies())
deps.put(dep, new HashSet<String>());
}
for (IAttribute ag : getAttributes().values())
addDependencyBundle(deps, ag);
if (!generators.isEmpty()) {
// Components appointed by generators are mandatory
//
for (IGenerator generator : generators.values()) {
String component = generator.getComponent();
if (component == null) {
addDependencyBundle(deps, getRequiredAttribute(generator.getAttribute()));
continue;
}
ComponentRequest dep = getDependency(component, null, null);
Set<String> attrs = deps.get(dep);
if (attrs == null) {
attrs = new HashSet<String>();
deps.put(dep, attrs);
}
attrs.add(generator.getAttribute());
}
}
List<QualifiedDependency> qDeps = new ArrayList<QualifiedDependency>(deps.size());
for (Map.Entry<ComponentRequest, Set<String>> entry : deps.entrySet())
qDeps.add(new QualifiedDependency(entry.getKey(), entry.getValue()));
return qDeps;
}
public Attribute getReferencedAttribute(String componentName, String componentType, VersionRange versionRange, String attributeName,
IModelCache ctx) throws CoreException {
CSpec referencedCSpec = getReferencedCSpec(componentName, componentType, versionRange, ctx);
if (referencedCSpec == null)
return null;
Attribute referencedAttr = referencedCSpec.getRequiredAttribute(attributeName);
if (referencedAttr.isEnabled(ctx)) {
if (referencedCSpec == this || referencedAttr.isPublic())
return referencedAttr;
throw new MissingAttributeException(referencedCSpec.getComponentIdentifier().toString(), attributeName, true);
}
return null;
}
public CSpec getReferencedCSpec(String componentName, String componentType, VersionRange versionRange, IModelCache ctx) throws CoreException {
if (componentName == null)
return this;
ComponentRequest dep = getDependency(componentName, componentType, versionRange);
if (!dep.isEnabled(ctx.getProperties()))
return null;
try {
return ctx.findCSpec(this, dep);
} catch (MissingComponentException e) {
if (dep.isOptional())
return null;
throw e;
}
}
public Attribute getRequiredAttribute(String name) throws MissingAttributeException {
Attribute attr = getAttribute(name);
if (attr == null)
throw new MissingAttributeException(componentIdentifier.toString(), name);
return attr;
}
@Override
public String getShortDesc() {
return shortDesc;
}
public String getTagInfo(String parentInfo) {
return getTagInfo(componentIdentifier, projectInfo, parentInfo);
}
@Override
public Version getVersion() {
return componentIdentifier.getVersion();
}
@Override
public boolean isPersisted(StorageManager sm) throws CoreException {
return sm.getCSpecs().contains(this);
}
public boolean isPruneApplicable(RMContext context, Map<String, ? extends Object> properties, boolean pruneForAttributes, Set<String> attrNames)
throws CoreException {
Collection<ComponentRequest> deps = getDependencies();
for (ComponentRequest dep : deps) {
Filter depFilter = dep.getFilter();
if (depFilter == null)
continue;
depFilter.addConsultedAttributes(context.getFilterAttributeUsageMap());
if (!depFilter.matchCase(properties))
//
// This dependency is pruned
//
return true;
}
// No dependency pruning occurred.
// Let's check for attribute pruning
//
Set<String> allAttrNames = getAttributes().keySet();
if (pruneForAttributes) {
if (attrNames.isEmpty())
attrNames = allAttrNames;
} else
attrNames = allAttrNames;
Set<String> referencedAttrNames = new HashSet<String>();
for (String attrName : attrNames) {
Attribute attr = getAttribute(attrName);
if (attr != null)
addReferencedDependencies(null, referencedAttrNames, attr, null);
}
return !allAttrNames.equals(referencedAttrNames);
}
public CSpec prune(RMContext context, Map<String, ? extends Object> properties, boolean pruneForAttributes, Set<String> attrNames)
throws CoreException {
if (!isPruneApplicable(context, properties, pruneForAttributes, attrNames))
return this;
CSpecBuilder bld = new CSpecBuilder();
bld.setComponentTypeID(getComponentTypeID());
bld.setDocumentation(getDocumentation());
bld.setName(getName());
bld.setProjectInfo(getProjectInfo());
bld.setShortDesc(getShortDesc());
bld.setVersion(getVersion());
bld.setFilter(getFilter());
Set<String> allAttrNames = getAttributes().keySet();
if (pruneForAttributes) {
if (attrNames.isEmpty())
attrNames = allAttrNames;
} else
attrNames = allAttrNames;
// Find all truly referenced dependencies. A dependency can
// be referenced from an attribute or from a generator
//
Collection<ComponentRequest> deps;
if (!pruneForAttributes || attrNames.isEmpty() && getGeneratorList().isEmpty())
deps = getDependencies();
else {
Set<ComponentRequest> referencedDeps = new HashSet<ComponentRequest>();
Set<String> referencedAttrs = new HashSet<String>();
for (String attrName : attrNames) {
Attribute attr = getAttribute(attrName);
if (attr != null)
addReferencedDependencies(referencedDeps, referencedAttrs, attr, null);
}
for (IGenerator generator : getGeneratorList()) {
String component = generator.getComponent();
if (component == null)
addReferencedDependencies(referencedDeps, referencedAttrs, getRequiredAttribute(generator.getAttribute()), null);
else
referencedDeps.add(getDependency(component, null, null));
}
deps = referencedDeps;
}
// Prune the dependencies according to LDAP filters. Add the
// dependencies
// that match to the new cspec
//
for (ComponentRequest dep : deps) {
Filter depFilter = dep.getFilter();
if (depFilter != null) {
depFilter.addConsultedAttributes(context.getFilterAttributeUsageMap());
if (!depFilter.matchCase(properties))
//
// This dependency is pruned
//
continue;
}
bld.addDependency(dep);
}
// Add all attributes that can fulfill the prerequisites transitively
// using
// the filtered dependency list
//
for (String attrName : attrNames) {
Attribute attr = getAttribute(attrName);
if (attr != null && dependenciesFulfilled(attr, bld, null))
bld.addAttribute(attr);
}
// Add all generators that can be added with respect to dependency or
// local attribute
//
for (IGenerator generator : getGeneratorList()) {
String component = generator.getComponent();
if (component == null) {
if (bld.getAttribute(generator.getAttribute()) == null)
//
// Local attribute no longer exists
//
continue;
} else if (bld.getDependency(component, null, null) == null)
//
// Dependency no longer exists
//
continue;
bld.addGenerator(generator);
}
return bld.createCSpec();
}
@Override
public void remove(StorageManager sm) throws CoreException {
UUID thisId = getId();
if (!sm.getResolutions().getReferencingKeys(thisId, "cspecId").isEmpty()) //$NON-NLS-1$
throw new ReferentialIntegrityException(this, "remove", Messages.Referenced_from_Resolution); //$NON-NLS-1$
sm.getCSpecs().removeElement(thisId);
}
@Override
public void store(StorageManager sm) throws CoreException {
sm.getCSpecs().putElement(this);
}
@Override
public void toSax(ContentHandler receiver) throws SAXException {
receiver.startDocument();
toSax(receiver, BM_CSPEC_NS, BM_CSPEC_PREFIX, getDefaultTag());
receiver.endDocument();
}
@Override
public String toString() {
return getComponentIdentifier().toString();
}
/**
* Verify that the specification is consistent. This method checks that all
* dependencies and attributes that are referenced from all prerequisites
* are present and that no circular references exists within the cspec.
*
* @throws MissingDependencyException
* @throws MissingAttributeException
* @throws CircularReferenceException
*/
public void verifyConsistency() throws MissingDependencyException, MissingAttributeException, CircularReferenceException {
// Verify the prerequisites of all attributes except
// the action artifacts (since their prerequisites
// will be equal to them of of the action that owns
// them).
//
for (Attribute attr : attributes.values())
if (!(attr instanceof IActionArtifact))
verifyPrerequisites(attr, null);
// Verify validity of generators
//
for (IGenerator generator : generators.values()) {
String component = generator.getComponent();
if (component == null)
getRequiredAttribute(generator.getAttribute());
else
getDependency(component, null, null);
}
}
private void verifyNonCircularDependency(Stack<String> seenAttributes, Collection<Prerequisite> prereqs, Stack<IAttributeFilter> filters)
throws MissingAttributeException, CircularReferenceException {
for (Prerequisite prereq : prereqs) {
if (prereq.isExternal())
//
// Can't go beyond this cspec at this stage
//
continue;
Attribute ag = getRequiredAttribute(prereq.getAttribute());
if (ag instanceof IActionArtifact)
ag = ((ActionArtifact) ag).getAction();
if (seenAttributes.contains(ag.getName()))
throw new CircularReferenceException(getComponentIdentifier().toString(), seenAttributes, ag.getName());
// Verify that this action dependency doesn't somehow
// stem from the action itself
//
List<Prerequisite> agPreqs;
if (prereq.isPatternFilter()) {
if (filters == null)
filters = new Stack<IAttributeFilter>();
filters.push(prereq);
agPreqs = ag.getPrerequisites(filters);
filters.pop();
} else
agPreqs = ag.getPrerequisites(filters);
if (agPreqs.size() > 0) {
seenAttributes.push(ag.getName());
verifyNonCircularDependency(seenAttributes, agPreqs, filters);
seenAttributes.pop();
}
}
}
private void verifyPrerequisites(Attribute attr, Stack<IAttributeFilter> filters) throws MissingDependencyException, MissingAttributeException,
CircularReferenceException {
Stack<String> seenActions = new Stack<String>();
for (Prerequisite prereq : attr.getPrerequisites(filters)) {
if (prereq.isExternal()) {
// Test that the dependency is present.
//
getDependency(prereq.getComponentName(), prereq.getComponentType(), prereq.getVersionRange());
continue;
}
Attribute ag = getRequiredAttribute(prereq.getAttribute());
if (ag instanceof IActionArtifact)
//
// Verify that we can get the action
//
((ActionArtifact) ag).getAction();
if (prereq.isPatternFilter()) {
if (filters == null)
filters = new Stack<IAttributeFilter>();
filters.push(prereq);
}
// Verify that this action dependency doesn't somehow
// stem from the action itself
//
Collection<Prerequisite> agPreqs = ag.getPrerequisites(filters);
if (agPreqs.size() > 0) {
seenActions.clear();
seenActions.push(ag.getName());
verifyNonCircularDependency(seenActions, agPreqs, filters);
}
if (prereq.isPatternFilter())
filters.pop();
}
}
}