package org.openprovenance.prov.template;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.Collections;
import java.util.Collection;
import org.openprovenance.prov.model.Document;
import org.openprovenance.prov.model.HasOther;
import org.openprovenance.prov.model.Identifiable;
import org.openprovenance.prov.model.Bundle;
import org.openprovenance.prov.model.Other;
import org.openprovenance.prov.model.ProvUtilities;
import org.openprovenance.prov.model.QualifiedName;
import org.openprovenance.prov.model.Statement;
import static org.openprovenance.prov.template.ExpandUtil.LINKED_URI;;
public class Groupings {
final private List<List<QualifiedName>> variables;
static ProvUtilities u= new ProvUtilities();
public Groupings() {
variables=new LinkedList<List<QualifiedName>>();
}
public List<QualifiedName> get(int group) {
return variables.get(group);
}
public int size() {
return variables.size();
}
public void addVariable(QualifiedName name) {
List<QualifiedName> ll=new LinkedList<QualifiedName>();
ll.add(name);
variables.add(ll);
}
public void addVariable(int group, QualifiedName name) {
List<QualifiedName> v=variables.get(group);
v.add(name);
}
@Override
public String toString () {
return "" + variables;
}
static public Groupings fromDocument(Document doc) {
Hashtable<QualifiedName,Set<QualifiedName>> linked=new Hashtable<QualifiedName, Set<QualifiedName>>();
Hashtable<QualifiedName,Integer> linkedGroups=new Hashtable<QualifiedName, Integer>();
Bundle bun=u.getBundle(doc).get(0);
Groupings grps=new Groupings();
Set<QualifiedName> allVars=new HashSet<QualifiedName>();
for (Statement statement: bun.getStatement()) {
Set<QualifiedName> vars=ExpandUtil.freeVariables(statement);
allVars.addAll(vars);
if (statement instanceof HasOther) {
HasOther stmt2=(HasOther)statement;
for (Other other: stmt2.getOther()) {
if (LINKED_URI.equals(other.getElementName().getUri())) {
QualifiedName id=((Identifiable)statement).getId();
QualifiedName otherId=(QualifiedName) other.getValue();
addEntry(linked, otherId, id);
addEntry(linked, id, otherId);
}
}
}
}
// Compute transitive closure
for(QualifiedName visit: Collections.list(linked.keys())) {
Stack<QualifiedName> toVisit = new Stack<QualifiedName>();
toVisit.push(visit);
Collection<QualifiedName> reachable = new HashSet<QualifiedName>();
while(toVisit.size()>0) {
QualifiedName local_qn = toVisit.pop();
for(QualifiedName neighbour: linked.get(local_qn)) {
if (!reachable.contains(neighbour)) {
reachable.add(neighbour);
toVisit.push(neighbour);
}
}
}
linked.get(visit).addAll(reachable);
}
QualifiedName [] sorted=allVars.toArray(new QualifiedName[0]);
Arrays.sort(sorted, new Comparator<QualifiedName>() {
@Override
public int compare(QualifiedName arg0, QualifiedName arg1) {
return arg0.getUri().compareTo(arg1.getUri());
}
});;
int currentGroup=0;
for (QualifiedName qn: sorted) {
Set<QualifiedName> links=linked.get(qn);
if (links==null || links.isEmpty()) {
grps.addVariable(qn);
} else {
Integer aGroup=linkedGroups.get(qn);
if (aGroup!=null) {
grps.addVariable(aGroup,qn);
} else {
grps.addVariable(qn);
for (QualifiedName otherQn: links) {
linkedGroups.put(otherQn,currentGroup);
}
}
}
currentGroup++;
}
return grps;
}
static void addEntry(Hashtable<QualifiedName, Set<QualifiedName>> linked,
QualifiedName id, QualifiedName otherId) {
if (linked.get(otherId)==null) {
linked.put(otherId,new HashSet<QualifiedName>());
}
linked.get(otherId).add(id);
}
}