/*
* @@COPYRIGHT@@
*/
package com.cosylab.acs.maci.manager;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import com.cosylab.acs.maci.ComponentInfo;
import com.cosylab.acs.maci.HandleConstants;
/**
* An implementation of topological sort operating on <code>ComponentInfo</code> handle data store.
*/
@SuppressWarnings("unchecked")
public class ComponentInfoTopologicalSort {
/**
* The tree nodes, packed into an array.
*/
protected ComponentInfoVertex[] nodes;
/**
* Number of used slots.
*/
protected int count = 0;
/**
* Handle to hash index lookups.
*/
protected IntHashMap handleToHashIndexMap;
/**
* Handle data store.
*/
protected HandleDataStore handleDataStore;
/**
* Create a Heap with the given initial capacity and comparator.
* @param capacity initial heap capacity.
* @param cmp comparator used to compare objects, if <code>null</code> natural ordering is used
* @exception IllegalArgumentException if capacity less than zero
*/
public ComponentInfoTopologicalSort(int capacity) throws IllegalArgumentException {
if (capacity < 0)
throw new IllegalArgumentException();
nodes = new ComponentInfoVertex[capacity];
// large load factor, no rehashing needed
handleToHashIndexMap = new IntHashMap(capacity, 2);
}
/**
* Constructor.
* @param dataStore <code>ComponentInfo</code> handle data store instance to sort.
*/
public ComponentInfoTopologicalSort(HandleDataStore dataStore) {
this(dataStore.size());
HashSet immortalChainMap = generateImmortalChainMap(dataStore);
int pos = 0;
for (int h = dataStore.first(); h != 0; h = dataStore.next(h))
{
ComponentInfo componentInfo = (ComponentInfo)dataStore.get(h);
this.insert(new ComponentInfoVertex(componentInfo, pos++, immortalChainMap.contains(componentInfo)));
}
this.handleDataStore = dataStore;
}
/**
* Generate immortal chain.
* @param dataStore <code>ComponentInfo</code> handle data store instance to sort.
* @return set of components (immportal or components which clients are immortal components).
*/
private HashSet generateImmortalChainMap(HandleDataStore dataStore)
{
HashSet set = new HashSet();
// TODO Manager.getHandle should be used here
markImmortalChain(set, dataStore, HandleConstants.MANAGER_MASK);
return set;
}
/**
* DSF algrithm to generate immortal chain.
* @param immortalChainMap set of components (immportal or components which clients are immortal components).
* @param dataStore <code>ComponentInfo</code> handle data store instance to sort.
* @param marker handle of the object which identifies member of immortal chain.
*/
private void markImmortalChain(HashSet immortalChainMap, HandleDataStore dataStore, int marker)
{
for (int h = dataStore.first(); h != 0; h = dataStore.next(h))
{
ComponentInfo componentInfo = (ComponentInfo)dataStore.get(h);
int[] clients = componentInfo.getClients().toArray();
for (int i = 0; i < clients.length; i++)
{
if (clients[i] == marker && !immortalChainMap.contains(componentInfo))
{
immortalChainMap.add(componentInfo);
markImmortalChain(immortalChainMap, dataStore, componentInfo.getHandle());
}
}
}
}
/**
* Return parent node of the child.
* @param k index of a child node.
*/
protected final int parent(int k) {
return (k - 1) / 2;
}
/**
* Return left child node.
* @param k index of a parent node.
*/
protected final int left(int k) {
return 2 * k + 1;
}
/**
* Return right child node.
* @param k index of a parent node.
*/
protected final int right(int k) {
return 2 * (k + 1);
}
/**
* Insert an element, resize if necessary.
* @param element object to be insterted.
*/
public void insert(ComponentInfoVertex element) {
// resize if necessary
if (count >= nodes.length) {
int newcap = 3 * nodes.length / 2 + 1;
ComponentInfoVertex[] newnodes = new ComponentInfoVertex[newcap];
System.arraycopy(nodes, 0, newnodes, 0, nodes.length);
nodes = newnodes;
}
// downheap
int k = count;
++count;
downheap(element, k);
}
/**
* @param element
* @param k
*/
protected void downheap(ComponentInfoVertex element, int k) {
while (k > 0) {
int par = parent(k);
if (element.compareTo(nodes[par]) < 0) {
nodes[k] = nodes[par];
handleToHashIndexMap.put(nodes[par].getComponentInfo().getHandle(), k);
k = par;
} else
break;
}
nodes[k] = element;
handleToHashIndexMap.put(element.getComponentInfo().getHandle(), k);
}
/**
* Return and remove least element, or null if empty.
* @return least element (min).
*/
protected ComponentInfoVertex heapExtract() {
if (count < 1)
return null;
// take element at root
int k = 0;
ComponentInfoVertex least = nodes[k];
handleToHashIndexMap.remove(least.getComponentInfo().getHandle());
--count;
// take last element, put it to root and do upheap
ComponentInfoVertex x = nodes[count];
nodes[count] = null;
for (;;) {
int l = left(k);
if (l >= count)
break;
else {
int r = right(k);
int child = (r >= count || nodes[l].compareTo(nodes[r]) < 0) ? l : r;
if (x.compareTo(nodes[child]) > 0) {
nodes[k] = nodes[child];
handleToHashIndexMap.put(nodes[child].getComponentInfo().getHandle(), k);
k = child;
} else
break;
}
}
nodes[k] = x;
handleToHashIndexMap.put(x.getComponentInfo().getHandle(), k);
return least;
}
/**
* Return and remove least element, or null if empty.
* @return least element (min).
*/
public ComponentInfoVertex extract() {
ComponentInfoVertex civ = heapExtract();
// for each child decrement indegree and heapify
if (!civ.getComponentInfo().getComponents().isEmpty())
{
int[] clients = civ.getComponentInfo().getComponents().toArray();
int civHandle = civ.getComponentInfo().getHandle();
for (int i = 0; i < clients.length; i++)
{
int index = handleToHashIndexMap.get(clients[i]);
if (index != -1)
{
if (nodes[index].getComponentInfo().getClients().contains(civHandle))
{
nodes[index].decrementIndegree();
downheap(nodes[index], index);
}
}
}
}
return civ;
}
/**
* Return least element without removing it, or null if empty
* @return least element (min).
**/
public ComponentInfoVertex peek() {
if (count > 0)
return nodes[0];
else
return null;
}
/**
* Return number of elements.
* @return number of elements
**/
public int size() {
return count;
}
/**
* Remove all elements.
**/
public void clear() {
for (int i = 0; i < count; ++i)
nodes[i] = null;
count = 0;
}
/**
* Topological sort on <code>ComponentInfo</code> handle data store.
* @param handleDataStore <code>ComponentInfo</code> handle data store
* @return topologically sorted list.
*/
public static List sort(HandleDataStore handleDataStore) {
ComponentInfoTopologicalSort ts = new ComponentInfoTopologicalSort(handleDataStore);
ArrayList list = new ArrayList(ts.size());
while (ts.size() > 0)
{
ComponentInfoVertex civ = ts.extract();
// if civ.getIndegree() != 0 then we have cycle (but we allow them)
list.add(civ.getComponentInfo());
}
return list;
}
/**
* Wrapper class around <code>ComponentInfo</code>.
*/
private static class ComponentInfoVertex implements Comparable {
/**
* Immortal credit (to increase indregree).
* Such components should be released after all non-importal components
* which are not subcomponents of moral components.
*/
private static final int IMMORTAL_CREDIT = Integer.MAX_VALUE / 2;
/**
* ComponentInfo instance itself.
*/
private ComponentInfo componentInfo;
/**
* Indegree (dependency) count.
*/
private int indegree = 0;
/**
* Activation order (position).
* Used to reconstruct reverse deactivation order.
*/
private int order;
/**
* Constructor.
* @param componentInfo instance to wrap.
*/
public ComponentInfoVertex(ComponentInfo componentInfo, int order, boolean immortalChain)
{
this.componentInfo = componentInfo;
this.order = order;
if (immortalChain)
indegree += IMMORTAL_CREDIT;
int[] clients = componentInfo.getClients().toArray();
for (int i = 0; i < clients.length; i++)
{
int type = clients[i] & HandleConstants.TYPE_MASK;
if (type == HandleConstants.COMPONENT_MASK)
indegree++;
}
}
/**
* Decrement (remove) one dependecy.
*/
public void decrementIndegree() {
indegree--;
}
/**
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object obj) {
int otherIndegree = ((ComponentInfoVertex)obj).indegree;
if (indegree < otherIndegree)
return -1;
else if (indegree == otherIndegree)
{
// reverse order
int otherOrder = ((ComponentInfoVertex)obj).order;
if (order < otherOrder)
return 1;
else if (order == otherOrder)
return 0;
else
return -1;
}
else
return 1;
}
/**
* Get wraped instance of <code>ComponentInfo</code>.
* @return returns the componentInfo.
*/
public ComponentInfo getComponentInfo() {
return componentInfo;
}
/**
* @return Returns the indegree.
*/
public int getIndegree() {
return indegree;
}
}
}