package org.archstudio.aim.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.archstudio.xadl.XadlUtils;
import org.archstudio.xadl3.domain_3_0.DomainType;
import org.archstudio.xadl3.domain_3_0.Domain_3_0Package;
import org.archstudio.xadl3.structure_3_0.Direction;
import org.archstudio.xarchadt.IXArchADT;
import org.archstudio.xarchadt.IXArchADTQuery;
import org.archstudio.xarchadt.ObjRef;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class AIMInstantiationOrderCalculator {
public static interface OrderedGroup {
public Iterable<ObjRef> getBrickRefs();
public Iterable<ObjRef> getLinkRefs();
}
private static class BrickNode implements OrderedGroup {
public ObjRef brickRef;
public Set<BrickNode> topBricks = Sets.newHashSet();
public Set<ObjRef> topLinksOut = Sets.newHashSet();
public Set<ObjRef> topLinksIn = Sets.newHashSet();
public Set<ObjRef> topLinksOther = Sets.newHashSet();
public Set<ObjRef> otherLinks = Sets.newHashSet();
public BrickNode(ObjRef brickRef) {
this.brickRef = brickRef;
}
@Override
public Iterable<ObjRef> getBrickRefs() {
return brickRef == null ? Collections.<ObjRef> emptyList() : Collections.singletonList(brickRef);
}
@Override
public Iterable<ObjRef> getLinkRefs() {
return Iterables.concat(topLinksOut, topLinksIn, topLinksOther, otherLinks);
}
}
/**
* Returns an ordered list of brick and links to instantiate such that:
* <ul>
* <li>bricks on "top" will be instantiated before bricks on "bottom"</li>
* <li>links directed "out" and on "top" will be welded before links directed "in" and on "bottom"</li>
* </ul>
*/
public static List<? extends OrderedGroup> calculateInstantiationOrder(IXArchADT xarch, ObjRef structureRef) {
List<BrickNode> brickNodes = Lists.newArrayList(calculateBrickGraph(xarch, structureRef));
List<BrickNode> sortedBrickNodeList = new ArrayList<BrickNode>();
while (!brickNodes.isEmpty()) {
Set<BrickNode> zeroDependencyBricks = Sets.newHashSet();
// Find all the (remaining) bricks with no top dependencies
for (BrickNode brickNode : brickNodes) {
if (brickNode.topBricks.size() == 0) {
zeroDependencyBricks.add(brickNode);
}
}
// at some point, we may end up with no zero dependency bricks left,
// in which case there is a cycle
if (zeroDependencyBricks.size() == 0) {
System.err.println("Cycle(s) detected in: ");
for (BrickNode n : brickNodes) {
System.err.println(" - " + xarch.get(n.brickRef, "name"));
}
zeroDependencyBricks.addAll(brickNodes);
}
// Move them to the sorted list
brickNodes.removeAll(zeroDependencyBricks);
sortedBrickNodeList.addAll(zeroDependencyBricks);
// Remove all the top-pointers to these bricks (as if we're slicing a layer off the graph)to top bricks
for (BrickNode brickNode : brickNodes) {
brickNode.topBricks.removeAll(zeroDependencyBricks);
}
// Now, on our next trip through the list, we will slice off the next set, and the next,
// and so on, until the sortedBrickNodeList contains all brick nodes
}
List<ObjRef> orderedBrickRefs = new ArrayList<ObjRef>(sortedBrickNodeList.size());
for (BrickNode bn : sortedBrickNodeList) {
orderedBrickRefs.add(bn.brickRef);
}
return sortedBrickNodeList;
}
private static Iterable<BrickNode> calculateBrickGraph(IXArchADT xarch, ObjRef structureRef) {
Map<ObjRef, BrickNode> brickNodes = Maps.newHashMap();
for (ObjRef brickRef : Iterables.filter(xarch.getAll(structureRef, "component"), ObjRef.class)) {
brickNodes.put(brickRef, new BrickNode(brickRef));
}
for (ObjRef brickRef : Iterables.filter(xarch.getAll(structureRef, "connector"), ObjRef.class)) {
brickNodes.put(brickRef, new BrickNode(brickRef));
}
// scan the links to populate the "top" fields of each brick
for (ObjRef linkRef : Iterables.filter(xarch.getAll(structureRef, "link"), ObjRef.class)) {
ObjRef iface1Ref = (ObjRef) xarch.get(linkRef, "point1");
ObjRef iface2Ref = (ObjRef) xarch.get(linkRef, "point2");
if (iface1Ref != null && iface2Ref != null) {
BrickNode brick1Node = brickNodes.get(xarch.getParent(iface1Ref));
BrickNode brick2Node = brickNodes.get(xarch.getParent(iface2Ref));
if (brick1Node != null && brick2Node != null) {
// see if iface1 is a "top" or "bottom" domain type
DomainType iface1DomainType = getDomain(xarch, iface1Ref);
DomainType iface2DomainType = getDomain(xarch, iface2Ref);
if (iface1DomainType == null) {
// if it is null, use the opposite domain of iface2's domain type
if (iface2DomainType != null) {
switch (iface2DomainType) {
case TOP:
iface1DomainType = DomainType.BOTTOM;
break;
case BOTTOM:
iface1DomainType = DomainType.TOP;
break;
}
}
}
// assign the "top" brick to the "bottom" brick's "top" list
if (iface1DomainType != null) {
// determine which brick is on "top"
BrickNode topBrickNode = brick1Node;
BrickNode bottomBrickNode = brick2Node;
ObjRef bottomIfaceRef = iface2Ref;
if (iface1DomainType == DomainType.TOP) {
topBrickNode = brick2Node;
bottomBrickNode = brick1Node;
bottomIfaceRef = iface1Ref;
}
bottomBrickNode.topBricks.add(topBrickNode);
// add the link to the "bottom" brick's "top" links
Direction bottomDir = (Direction) xarch.get(bottomIfaceRef, "direction");
if (bottomDir != null) {
switch (bottomDir) {
case IN:
bottomBrickNode.topLinksIn.add(linkRef);
continue;
case OUT:
bottomBrickNode.topLinksOut.add(linkRef);
continue;
default:
throw new IllegalArgumentException();
}
}
bottomBrickNode.topLinksOther.add(linkRef);
}
brick1Node.otherLinks.add(linkRef);
}
}
}
return brickNodes.values();
}
public static DomainType getDomain(IXArchADTQuery xarch, ObjRef interfaceRef) {
ObjRef domainExtRef = XadlUtils.getExt(xarch, interfaceRef, Domain_3_0Package.eNS_URI, "DomainExtension");
if (domainExtRef != null) {
ObjRef domainRef = (ObjRef) xarch.get(domainExtRef, "domain");
if (domainRef != null) {
DomainType domainType = (DomainType) xarch.get(domainRef, "type");
return domainType;
}
}
return null;
}
}