package agg.xt_basis;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import agg.attribute.impl.TupleMapping;
import agg.xt_basis.colim.ALPHA_DIAGRAM;
import agg.xt_basis.colim.COLIM_DEFS;
import agg.xt_basis.colim.COLIM_VECTOR;
import agg.xt_basis.colim.INT_VECTOR;
import agg.xt_basis.colim.SET_DIAGRAM;
/**
* This class allows for representation of general diagrams of graphs and for
* computation of their colimit. It has capabilities for optional in-place
* computation of the colimit object in one of the diagram nodes. Attributes are
* ignored for colimit computation. The colimit computation itself is
* implemented using the colimit library from Dietmar Wolz.
*/
public class ColimDiagram implements COLIM_DEFS {
/**
* Construct myself to be an empty diagram where the colimit object is to be
* computed into the given Graph <code>result</code>. By adding
* <code>result</code> as an ordinary diagram node via
* <code>addNode</code> as well, in-place computation can be achieved.
* <p>
* <b>Pre:</b> <code>result.isGraph()</code>.
*/
public ColimDiagram(Graph result) {
this.itsDiagram = new ALPHA_DIAGRAM();
this.itsColimGraph = result;
this.itsColimMorphisms = new Vector<OrdinaryMorphism>();
this.itsGraphIndexMap = new Hashtable<Graph, Integer>(8);
this.itsInplaceFlag = false;
}
/**
* Add a Graph as a node to the diagram.
* <p>
* <b>Pre:</b> <code>graph.isGraph()</code>.
*/
public void addNode(Graph graph) {
// Objects of the graph (element type GraphObject):
COLIM_VECTOR allObjects = new COLIM_VECTOR(32);
// src, tar, abs references of each object (element type INT_VECTOR):
COLIM_VECTOR allObjectsRefs = new COLIM_VECTOR(32);
// Attributes/Type (?) of each object (element type COLIM_VECTOR of
// Type):
COLIM_VECTOR allObjectsAttrs = new COLIM_VECTOR(32);
// Mapping of graph objects to their index in diagram representation
// (maps GraphObject to Integer):
Dictionary<GraphObject, Integer> anIndexMap = new Hashtable<GraphObject, Integer>(32);
if (graph == this.itsColimGraph)
this.itsInplaceFlag = true;
// fill allObjects:
fillObjects(allObjects, allObjectsRefs, allObjectsAttrs, anIndexMap,
graph.getNodesSet().iterator());
fillObjects(allObjects, allObjectsRefs, allObjectsAttrs, anIndexMap,
graph.getArcsSet().iterator());
/*
* // fill allObjects allObjects.ensureCapacity(graph.getSize());
* Enumeration anObjectIter = graph.getElements(); for (int i = 0;
* anObjectIter.hasMoreElements(); i++){ anObject = (GraphObject)
* anObjectIter.nextElement(); allObjects.push_back(anObject); } // fill
* allObjectsRefs: allObjectsRefs.ensureCapacity(allObjects.size()); for
* (int i = 0; i < allObjects.size(); i++){ GraphObject anObject = (GraphObject)
* allObjects.at(i); INT_VECTOR anObjectsRefs = new INT_VECTOR(2);
*
* if (anObject.isNode()){ // set source and target references to
* undefined: anObjectsRefs.push_back(i); anObjectsRefs.push_back(i); }
* else{ anObjectsRefs.push_back(((Integer) anIndexMap.get(((Arc)
* anObject).getSource())).intValue());
* anObjectsRefs.push_back(((Integer) anIndexMap.get(((Arc)
* anObject).getTarget())).intValue()); }
* allObjectsRefs.push_back(anObjectsRefs);
*
* COLIM_VECTOR anObjectsAttrs = new COLIM_VECTOR(1);
* anObjectsAttrs.push_back(anObject.getType());
* allObjectsAttrs.push_back(anObjectsAttrs); }
*/
allObjects.trimToSize();
allObjectsRefs.trimToSize();
allObjectsAttrs.trimToSize();
int i = this.itsDiagram.insert_object(allObjects, allObjectsRefs,
allObjectsAttrs, graph.getName());
this.itsGraphIndexMap.put(graph, new Integer(i));
}
private void fillObjects(
final COLIM_VECTOR allObjects,
final COLIM_VECTOR allObjectsRefs,
final COLIM_VECTOR allObjectsAttrs,
final Dictionary<GraphObject, Integer> anIndexMap,
final Iterator<?> anObjectIter) {
// fill allObjects and allObjectsRefs
int count = allObjects.size();
for (int i = count; anObjectIter.hasNext(); i++) {
GraphObject anObject = (GraphObject)anObjectIter.next();
allObjects.push_back(anObject);
anIndexMap.put(anObject, new Integer(i));
INT_VECTOR anObjectsRefs = new INT_VECTOR(2);
if (anObject.isNode()) {
// set source and target references to undefined:
anObjectsRefs.push_back(i);
anObjectsRefs.push_back(i);
anObjectsRefs.trimToSize();
} else {
anObjectsRefs.push_back(anIndexMap.get(
((Arc) anObject).getSource()).intValue());
anObjectsRefs.push_back(anIndexMap.get(
((Arc) anObject).getTarget()).intValue());
anObjectsRefs.trimToSize();
}
allObjectsRefs.push_back(anObjectsRefs);
COLIM_VECTOR anObjectsAttrs = new COLIM_VECTOR(1);
anObjectsAttrs.push_back(anObject.getType());
anObjectsAttrs.trimToSize();
allObjectsAttrs.push_back(anObjectsAttrs);
}
}
/**
* Add an Morphism as an edge to the diagram.
* <p>
* <b>Pre:</b> <code>morph.getOriginal()</code> and
* <code>morph.getImage()</code> have been added to the diagram with
* <code>addNode()</code> before.
*
* @see agg.xt_basis.Morphism#getOriginal()
* @see agg.xt_basis.Morphism#getImage()
* @see agg.xt_basis.ColimDiagram#addNode
*/
public void addEdge(Morphism morph) {
Graph aSourceGraph = morph.getOriginal();
Graph aTargetGraph = morph.getImage();
GraphObject anObject = null;
Dictionary<GraphObject, Integer>
aTargetIndexMap = new Hashtable<GraphObject, Integer>(32);
// maps GraphObject to Integer
int count = 0;
Iterator<?> anObjectIter = aTargetGraph.getNodesSet().iterator();
for (int i= count; anObjectIter.hasNext(); i++) {
aTargetIndexMap.put((GraphObject) anObjectIter.next(),
new Integer(i));
}
count = aTargetIndexMap.size();
anObjectIter = aTargetGraph.getArcsSet().iterator();
for (int i = count; anObjectIter.hasNext(); i++) {
aTargetIndexMap.put((GraphObject) anObjectIter.next(),
new Integer(i));
}
INT_VECTOR aMorphism = new INT_VECTOR(64);
anObjectIter = aSourceGraph.getNodesSet().iterator();
while (anObjectIter.hasNext()) {
anObject = morph.getImage((GraphObject) anObjectIter.next());
if (anObject != null) {
if (aTargetIndexMap.get(anObject) != null)
aMorphism.push_back(aTargetIndexMap.get(anObject)
.intValue());
else
aMorphism.push_back(undefined);
} else
aMorphism.push_back(undefined);
}
anObjectIter = aSourceGraph.getArcsSet().iterator();
while (anObjectIter.hasNext()) {
anObject = morph.getImage((GraphObject) anObjectIter.next());
if (anObject != null) {
if (aTargetIndexMap.get(anObject) != null)
aMorphism.push_back(aTargetIndexMap.get(anObject)
.intValue());
else
aMorphism.push_back(undefined);
} else
aMorphism.push_back(undefined);
}
this.itsDiagram.insert_morphism(aMorphism, this.itsGraphIndexMap
.get(aSourceGraph).intValue(), this.itsGraphIndexMap.get(
aTargetGraph).intValue());
}
/**
* Perform the colimit computation for the diagram I'm representing. The
* Graph <code>result</code> which has been passed to my constructor
* becomes the colimit object, and the colimit morphisms requested by
* <code>requestEdge()</code> are built accordingly.
*
* @see agg.xt_basis.ColimDiagram#requestEdge
*/
public final void computeColimit() throws TypeException {
this.adoptEntriesWhereEmpty = false;
COLIM_VECTOR items = this.itsDiagram.get_colimit_items_total();
COLIM_VECTOR refs = this.itsDiagram.get_colimit_refs_total();
// we don't actually use attrs
COLIM_VECTOR attrs = this.itsDiagram.get_colimit_attrs_total();
// this may throw a TypeException
try {
convertColimit(items, refs, attrs);
} catch (TypeException ex) {
throw ex;
}
}
public final void computeColimit(boolean adoptEntries) throws TypeException {
this.adoptEntriesWhereEmpty = adoptEntries;
COLIM_VECTOR items = this.itsDiagram.get_colimit_items_total();
COLIM_VECTOR refs = this.itsDiagram.get_colimit_refs_total();
// we don't actually use attrs
COLIM_VECTOR attrs = this.itsDiagram.get_colimit_attrs_total();
// this may throw a TypeException
try {
convertColimit(items, refs, attrs);
} catch (TypeException ex) {
throw ex;
}
}
/**
* Request the computation of the given empty morphism as a colimit
* morphism.
* <p>
* <b>Pre:</b>
* <ol>
* <li>The domain of <code>morph</code> is empty.
* <li><code>morph.getOriginal()</code> has been added to the diagram via
* <code>addNode()</code>.
* <li><code>morph.getImage()</code> is the <code>result</code> object
* that has been passed to my constructor.
* </ol>
*
* @see agg.xt_basis.OrdinaryMorphism#getOriginal()
* @see agg.xt_basis.OrdinaryMorphism#getImage()
* @see agg.xt_basis.ColimDiagram#addNode
*/
public final void requestEdge(OrdinaryMorphism morph) {
Graph aTargetGraph = morph.getImage();
if (aTargetGraph == this.itsColimGraph) {
this.itsColimMorphisms.add(morph);
}
}
@SuppressWarnings("rawtypes")
private final void convertColimit(COLIM_VECTOR items, COLIM_VECTOR refs,
COLIM_VECTOR attrs) throws TypeException {
SET_DIAGRAM anItemDiagram = this.itsDiagram.get_item_diagram();
// The Vector "allColimItems" is computed following different
// strategies in the two cases "inplace" and "diagram".
// Once it has been built, it behaves like this:
// for every index i into the colimit vector "items",
// allColimItems.elementAt(i) will get us the corresponding
// GraphObject in "itsColimGraph".
Vector<Vector<GraphObject>> allColimItems = null;
if (this.itsInplaceFlag) {
// A Vector to represent the inverse relation of the implicit
// morphism between "itsColimGraph" and the computed colimit
// object represented by "items". Elements are of type
// Vector of GraphObject.
Vector<Vector<GraphObject>> allOrigs = new Vector<Vector<GraphObject>>(
items.size());
// initialize "allOrigs" with empty vectors:
for (int i = 0; i < items.size(); i++) {
allOrigs.addElement(new Vector<GraphObject>(2));
}
// loop over all GraphObjects in "itsColimGraph", computing
// the inverse image Vector "allOrigs".
// On the fly, delete GraphObjects that are not mapped.
int anImgIndex;
GraphObject anOrigObj;
int aColimGraphIndex = this.itsGraphIndexMap.get(this.itsColimGraph)
.intValue();
int aFirstIndex = anItemDiagram.set_at_node(aColimGraphIndex).lower;
int aLastIndex = anItemDiagram.set_at_node(aColimGraphIndex).upper;
final Vector<Node> nodestodestroy = new Vector<Node>();
for (int anObjIndex = aFirstIndex; anObjIndex <= aLastIndex; anObjIndex++) {
anOrigObj = (GraphObject) anItemDiagram.get_element(anObjIndex);
anImgIndex = anItemDiagram.get_colimit_pos(anObjIndex);
if (anImgIndex == undefined) {
if (anOrigObj.isNode()) {
// undo here
// itsColimGraph.destroyNode((Node) anOrigObj, false);
// put nodes without img here and do destroy later
nodestodestroy.add((Node) anOrigObj);
} else {
// undo here
this.itsColimGraph.destroyArc((Arc) anOrigObj, false, false);
}
} else {
// remained objects
allOrigs.elementAt(anImgIndex).addElement(anOrigObj);
}
}
// destroy nodes without img
for (int d=0; d<nodestodestroy.size(); d++) {
this.itsColimGraph.destroyNode(nodestodestroy.get(d), false, false);
}
// Now that we have computed the inverse image, we can determine
// which objects are to be kept (only updated in their attributes),
// which objects to merge (if we have non-injective
// morphisms in the colimit diagram),
// and which objects have to be created.
// This is done in a loop over the elements of the vector
// "items" that represents the colimit object:
Vector<GraphObject> aCurOrigs; // The GraphObjects contained in
// this vector
// represent the inverse image of the currently
// processed element of the colimit object
INT_VECTOR anObjectsRefs;
Type aType;
Vector<GraphObject> aSrcOrigs, aTarOrigs; // similar to aCurOrigs
boolean anObjectLeftToCreate; // flag if there's an Object left to
// create
do {
anObjectLeftToCreate = false;
for (int i = 0; i < items.size(); i++) {
aCurOrigs = allOrigs.elementAt(i);
switch (aCurOrigs.size()) {
case 0:
// a new GraphObject has to be created:
anObjectsRefs = (INT_VECTOR) refs.item(i);
aSrcOrigs = allOrigs.elementAt(anObjectsRefs.item(0));
aTarOrigs = allOrigs.elementAt(anObjectsRefs.item(1));
GraphObject gobj = (GraphObject) items.item(i);
aType = gobj.getType();
if (gobj.getContext().getTypeSet() != this.itsColimGraph
.getTypeSet())
aType = this.itsColimGraph.getTypeSet().getSimilarType(
gobj.getType());
try {
if (gobj.isNode()) {
// create a node:
Node n = this.itsColimGraph.createNode(aType);
n.setContextUsage(gobj.getContextUsage());
allOrigs.elementAt(i).addElement(n);
this.createdNodes.add(n);
// // undo here
} else {
// create an arc
if ((aSrcOrigs.size() > 0)
&& (aTarOrigs.size() > 0)) {
// src and tar objects exist -
Arc a = this.itsColimGraph.createArc(aType,
(Node) aSrcOrigs.firstElement(),
(Node) aTarOrigs.firstElement());
allOrigs.elementAt(i).addElement(a);
// undo here
// if the new arc brokes the multiplicity
// condition a TypeException will be thrown
} else
anObjectLeftToCreate = true;
}
} catch (TypeException ex) {
throw (ex);
}
// Note: The vectors a***Origs contain more than
// one element only if there is a non-injective
// morphism in the colim diagram. In this case, we
// have to merge all this elements into one. We
// merge into the memory location of the first
// element, thus we can mindlessly use
// firstElement() on the vectors for abs, src, tar
// parameters above. (The merging is done in the
// switch default case.)
// else anObjectLeftToCreate = true;
break;
case 1:
// keep this object!");
// the object does already exist.
break;
default:
// merge....
for (int j = 1; j < aCurOrigs.size(); j++) {
this.itsColimGraph.glue(aCurOrigs.get(0),
aCurOrigs.get(j));
}
}
} // for
} while (anObjectLeftToCreate);
allColimItems = allOrigs;
} else {
System.out
.println("agg.xt_basis.ColimDiagram: Sorry, only inplace computation is supported yet");
return;
}
// compute requested colimit morphisms:
OrdinaryMorphism aColimMorph;
GraphObject aSrc, aTar;
Enumeration<OrdinaryMorphism> aMorphIter;
int aSrcGraph, aTarPos;
// Loop over all colimit morphisms:
for (aMorphIter = this.itsColimMorphisms.elements(); aMorphIter
.hasMoreElements();) {
aColimMorph = aMorphIter.nextElement();
aSrcGraph = this.itsGraphIndexMap.get(aColimMorph.getOriginal())
.intValue();
// Loop over all GraphObjects in source graph of morphism:
for (int anObjIndex = anItemDiagram.set_at_node(aSrcGraph).lower; anObjIndex <= anItemDiagram
.set_at_node(aSrcGraph).upper; anObjIndex++) {
aTarPos = anItemDiagram.get_colimit_pos(anObjIndex);
if (aTarPos != undefined) {
aSrc = (GraphObject) anItemDiagram.get_element(anObjIndex);
// aTar = (GraphObject) allColimItems.elementAt(aTarPos);
aTar = (GraphObject) ((Vector) allColimItems
.elementAt(aTarPos)).firstElement();
try {
aColimMorph.addMapping(aSrc, aTar);
} catch (BadMappingException ex) {
return;
}
if (this.adoptEntriesWhereEmpty)
this.adoptEntriesWhereEmpty(aColimMorph, aSrc, aTar);
}
}
}
}
private void adoptEntriesWhereEmpty(OrdinaryMorphism morph,
GraphObject from, GraphObject to) {
if (!morph.getImage().isAttributed()
|| (from.getAttribute() == null)
|| (to.getAttribute() == null))
return;
if (morph.getImage(from) != null) {
agg.attribute.impl.ContextView context = (agg.attribute.impl.ContextView) morph
.getAttrContext();
Vector<TupleMapping> mappings = context
.getMappingsToTarget((agg.attribute.impl.ValueTuple) to
.getAttribute());
if (mappings != null) {
mappings.elementAt(0).adoptEntriesWhereEmpty(
(agg.attribute.impl.ValueTuple) from
.getAttribute(),
(agg.attribute.impl.ValueTuple) to
.getAttribute());
}
}
}
public Vector<GraphObject> getCreatedNodes() {
return this.createdNodes;
}
// ---- member variables -----------------------------------------
/**
* The graph that becomes the resulting colimit graph.
*/
private Graph itsColimGraph;
/**
* A mapping of graphs to the indices of their representing nodes in
* <code>itsDiagram</code>. Keys are of type <code>Graph</code>,
* values of type <code>Integer</code>.
*/
private Dictionary<Graph, Integer> itsGraphIndexMap;
private ALPHA_DIAGRAM itsDiagram;
private Vector<OrdinaryMorphism> itsColimMorphisms;
private boolean itsInplaceFlag;
private boolean adoptEntriesWhereEmpty = false;
private final Vector<GraphObject> createdNodes = new Vector<GraphObject>();
}