// 2. Create nodes for new concepts and connect to node hierarchy
// a. First create the nodes and add to index
for (IntIterator itr = allNew.keyIterator(); itr.hasNext();) {
final String key = factory.lookupConceptId(itr.next()).toString();
Node cn = new Node();
cn.getEquivalentConcepts().add(key);
conceptNodeIndex.put(key, cn);
}
// b. Now connect the nodes disregarding redundant connections
Node bottomNode = conceptNodeIndex.get(au.csiro.ontology.model.NamedConcept.BOTTOM);
for (IntIterator itr = allNew.keyIterator(); itr.hasNext();) {
int id = itr.next();
final String key = factory.lookupConceptId(id).toString();
Node cn = conceptNodeIndex.get(key);
IConceptSet parents = allNew.get(id);
for (IntIterator itr2 = parents.iterator(); itr2.hasNext();) {
// Create a connection to each parent
int parentId = itr2.next();
if (parentId == id)
continue;
Node parent = conceptNodeIndex.get(factory.lookupConceptId(parentId));
cn.getParents().add(parent);
parent.getChildren().add(cn);
// All nodes that get new children and are connected to BOTTOM
// must be disconnected
if (parent.getChildren().contains(bottomNode)) {
parent.getChildren().remove(bottomNode);
bottomNode.getParents().remove(parent);
}
}
}
Set<Integer> toRemoveFromAffected = new HashSet<Integer>();
for (IntIterator itr = allAffected.keyIterator(); itr.hasNext();) {
final int id = itr.next();
final String key = factory.lookupConceptId(id).toString();
Node cn = conceptNodeIndex.get(key);
IConceptSet parents = allAffected.get(id);
if(parents.contains(IFactory.BOTTOM_CONCEPT)) {
// Special case - bottom is parent
// a. add equivalents to bottom node
bottomNode.getEquivalentConcepts().addAll(cn.getEquivalentConcepts());
Set<Node> tempParents = cn.getParents();
Set<Node> tempChildren = cn.getChildren();
// b. reconnect parents to children
for(Node parent : tempParents) {
parent.getChildren().remove(cn);
parent.getChildren().addAll(tempChildren);
}
for(Node child : tempChildren) {
child.getParents().remove(cn);
child.getParents().addAll(tempParents);
}
for(String k : cn.getEquivalentConcepts()) {
conceptNodeIndex.remove(k);
conceptNodeIndex.put(key, bottomNode);
}
toRemoveFromAffected.add(id);
} else {
for (IntIterator itr2 = parents.iterator(); itr2.hasNext();) {
// Create a connection to each parent
int parentId = itr2.next();
if (parentId == id)
continue;
Node parent = conceptNodeIndex.get(factory.lookupConceptId(parentId));
cn.getParents().add(parent);
parent.getChildren().add(cn);
// All nodes that get new children and are connected to BOTTOM must be disconnected
if (parent.getChildren().contains(bottomNode)) {
parent.getChildren().remove(bottomNode);
bottomNode.getParents().remove(parent);
}
}
}
}
for(Integer i : toRemoveFromAffected) {
allAffected.remove(i.intValue());
allNew.remove(i.intValue());
}
// 3. Connect new nodes without parents to TOP
Node topNode = conceptNodeIndex.get(au.csiro.ontology.model.NamedConcept.TOP);
for (IntIterator itr = allNew.keyIterator(); itr.hasNext();) {
final String key = factory.lookupConceptId(itr.next()).toString();
Node cn = conceptNodeIndex.get(key);
if (cn.getParents().isEmpty()) {
cn.getParents().add(topNode);
topNode.getChildren().add(cn);
}
}
// 4. Fix connections for new and affected concepts
// a. Check for equivalents
Set<Pair> pairsToMerge = new HashSet<Pair>();
for (IntIterator itr = allNew.keyIterator(); itr.hasNext();) {
final String key = factory.lookupConceptId(itr.next()).toString();
Node cn = conceptNodeIndex.get(key);
for (Node parent : cn.getParents()) {
if (parent.getParents().contains(cn)) {
pairsToMerge.add(new Pair(cn, parent));
}
}
}
for (IntIterator itr = allAffected.keyIterator(); itr.hasNext();) {
final String key = factory.lookupConceptId(itr.next()).toString();
Node cn = conceptNodeIndex.get(key);
for (Node parent : cn.getParents()) {
if (parent.getParents().contains(cn)) {
pairsToMerge.add(new Pair(cn, parent));
}
}
}
Set<Node> affectedByMerge = new HashSet<Node>();
// Merge equivalents
for (Pair p : pairsToMerge) {
Node cn1 = p.getA();
Node cn2 = p.getB();
affectedByMerge.addAll(cn1.getChildren());
affectedByMerge.addAll(cn2.getChildren());
// Merge into cn1 - remove cn2 from index and replace with cn1
for (String n : cn2.getEquivalentConcepts()) {
conceptNodeIndex.put(n, cn1);
}
cn1.getEquivalentConcepts().addAll(cn2.getEquivalentConcepts());
// Remove relationships between merged concepts
cn1.getParents().remove(cn2);
cn2.getChildren().remove(cn1);
cn2.getParents().remove(cn1);
cn1.getChildren().remove(cn2);
// Taxonomy is bidirectional
cn1.getParents().addAll(cn2.getParents());
for (Node parent : cn2.getParents()) {
parent.getChildren().remove(cn2);
parent.getChildren().add(cn1);
}
cn1.getChildren().addAll(cn2.getChildren());
for (Node child : cn2.getChildren()) {
child.getParents().remove(cn2);
child.getParents().add(cn1);
}
cn2 = null; // nothing should reference cn2 now
}
// b. Fix all new and affected nodes
Set<Node> all = new HashSet<Node>();
for (IntIterator it = allNew.keyIterator(); it.hasNext();) {
all.add(conceptNodeIndex.get(factory.lookupConceptId(it.next())));
}
for (IntIterator it = allAffected.keyIterator(); it.hasNext();) {
all.add(conceptNodeIndex.get(factory.lookupConceptId(it.next())));
}
for (Node cn : affectedByMerge) {
all.add(cn);
}
// Add also the children of the affected nodes
Set<Node> childrenToAdd = new HashSet<Node>();
for (Node cn : all) {
for (Node ccn : cn.getChildren()) {
if (ccn.equals(bottomNode))
continue;
childrenToAdd.add(ccn);
}
}
all.addAll(childrenToAdd);
// Find redundant relationships
for (Node cn : all) {
Set<Node> ps = cn.getParents();
Object[] parents = ps.toArray(new Object[ps.size()]);
Set<Node> toRemove = new HashSet<Node>();
for (int i = 0; i < parents.length; i++) {
for (int j = i + 1; j < parents.length; j++) {
if (isChild((Node)parents[j], (Node)parents[i])) {
toRemove.add((Node)parents[i]);
continue;
}
if (isChild((Node)parents[i], (Node)parents[j])) {
toRemove.add((Node)parents[j]);
continue;
}
}
}
for (Node tr : toRemove) {
cn.getParents().remove(tr);
tr.getChildren().remove(cn);
}
}
// 5. Connect bottom to new and affected concepts with no children
for (IntIterator itr = allNew.keyIterator(); itr.hasNext();) {
final int key = itr.next();
Node cn = conceptNodeIndex.get(factory.lookupConceptId(key));
if (cn.getChildren().isEmpty()) {
cn.getChildren().add(bottomNode);
bottomNode.getParents().add(cn);
}
}
for (IntIterator itr = allAffected.keyIterator(); itr.hasNext();) {
final int key = itr.next();
Node cn = conceptNodeIndex.get(factory.lookupConceptId(key));
if (cn.getChildren().isEmpty()) {
cn.getChildren().add(bottomNode);
bottomNode.getParents().add(cn);
}
}
// 6. Connect the top node to new and affected concepts with no parents
for (IntIterator itr = allNew.keyIterator(); itr.hasNext();) {
final int key = itr.next();
Node cn = conceptNodeIndex.get(factory.lookupConceptId(key));
if (cn.getParents().isEmpty()) {
cn.getParents().add(topNode);
topNode.getChildren().add(cn);
}
}
for (IntIterator itr = allAffected.keyIterator(); itr.hasNext();) {
final int key = itr.next();
Node cn = conceptNodeIndex.get(factory.lookupConceptId(key));
if (cn.getParents().isEmpty()) {
cn.getParents().add(topNode);
topNode.getChildren().add(cn);
}
}
}
}