package org.aksw.jena_sparql_api.views;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.aksw.commons.collections.multimaps.BiHashMultimap;
import org.aksw.commons.collections.multimaps.IBiSetMultimap;
import org.aksw.commons.collections.multimaps.MultimapUtils;
import com.google.common.collect.Sets;
/**
* A map where keys can be stated as equivalent.
*
* Provides methods for checking consistency (the map gets inconsistent
* as soon as a key maps to multiple non-equal values)
*
*
* TODO Not sure if a conflict resultion strategy for the case that
* values are not directly equal but not contradictory should go here.
* For instance if a key x maps to {A, B} and y maps to {A} one resolution stragtegy
* might be to map both to A.
*
* Essentially I have the ValueSets in mind to quickly reject inconsistent bindings.
*
* @author raven
*
* @param <K>
* @param <V>
*/
public class EquiMap<K, V>
{
private IBiSetMultimap<K, K> equivalences = new BiHashMultimap<K, K>();
private Map<K, V> keyToValue = new HashMap<K, V>();
/**
*
* @return A view of all keys
*/
public Set<K> keySet() {
return Sets.union(equivalences.asMap().keySet(), keyToValue.keySet());
}
public void clear()
{
equivalences.clear();
keyToValue.clear();
}
public boolean isEqual(K a, K b) {
return get(a).contains(b);
}
public boolean areAllEqual(Collection<K> set) {
if(set.isEmpty()) {
return true; // Is that sane? Should it be false?
}
return get(set.iterator().next()).containsAll(set);
}
public boolean isConsistentInsertEquiv(K a, K b)
{
return isConsistentSet(Sets.union(get(a), get(b)));
}
public boolean isConsistentSet(Set<V> set)
{
return set.size() <= 1;
}
public boolean isConsistentInsertValue(K a, V b)
{
Set<V> values = get(a);
if(values.isEmpty()) {
return true;
} else if(values.size() == 1 && values.contains(b)) {
return true;
} else if(values.size() > 1) {
throw new RuntimeException("Should not happen");
}
return false;
}
public boolean isSelfConsistent()
{
Set<K> open = new HashSet<K>(keyToValue.keySet());
while(!open.isEmpty()) {
K key = open.iterator().next();
open.remove(key);
Set<K> keys = getEquivalences(key, false);
open.removeAll(keys);
Set<V> values = get(keys);
if(!isConsistentSet(values)) {
return false;
}
}
return true;
}
public EquiMap()
{
}
public EquiMap(EquiMap<K, V> other)
{
for(Map.Entry<K, Collection<K>> entry : equivalences.asMap().entrySet()) {
for(K value : entry.getValue()) {
this.equivalences.put(entry.getKey(), value);
}
}
this.keyToValue.putAll(other.keyToValue);
}
public IBiSetMultimap<K, K> getEquivalences()
{
return equivalences;
}
public Map<K, V> getKeyToValue()
{
return keyToValue;
}
public void put(K key, V value)
{
keyToValue.put(key, value);
}
public Set<V> getAll(Set<?> keys)
{
Set<V> result = new HashSet<V>();
for(Object key : keys) {
if(keyToValue.containsKey(key)) {
result.add(keyToValue.get(key));
}
}
return result;
}
public Set<K> getEquivalences(Object key, boolean reflexiv)
{
Set<K> result = MultimapUtils.transitiveGetBoth(equivalences, key);
if(reflexiv) {
result.add((K)key);
}
return result;
}
public Set<K> getAllEquivalences(Collection<?> keys, boolean reflexiv)
{
Set<K> result = new HashSet<K>();
Set<Object> open = new HashSet<Object>(keys);
while(!open.isEmpty()) {
Object key = open.iterator().next();
open.remove(key);
Set<K> equivs = getEquivalences(key, reflexiv);
open.removeAll(equivs);
result.addAll(equivs);
}
return result;
}
public Set<V> get(Object key)
{
return getAll(getEquivalences(key, true));
}
public boolean makeEqual(K a, K b)
{
return equivalences.put(a, b);
}
/**
* Checks whether the union of two equimaps is again consistent.
*
*
* @param other
* @return
*/
public boolean isCompatible(EquiMap<K, V> other)
{
// We have to check whether any of the keys refer to multiple distinct variables
// So we need to check for each key that has a value attached
Set<K> open = new HashSet<K>(keyToValue.keySet());
while(!open.isEmpty()) {
K key = open.iterator().next();
open.remove(key);
Set<K> bothEquivs = new HashSet<K>();
//bothEquivs.add(key); redundant
// We need to traverse the bipartite graph is order to collect
// all equivalences
//
//
Set<K> newEquivs = Collections.singleton(key);
do {
Set<K> thisEquivs = getAllEquivalences(newEquivs, true);
thisEquivs.removeAll(bothEquivs);
open.removeAll(thisEquivs);
bothEquivs.addAll(thisEquivs);
newEquivs = other.getAllEquivalences(thisEquivs, true);
newEquivs.removeAll(bothEquivs);
open.removeAll(newEquivs);
bothEquivs.addAll(newEquivs);
} while(!newEquivs.isEmpty());
Set<V> thisValues = getAll(bothEquivs);
Set<V> otherValues = other.getAll(bothEquivs);
Set<V> union = Sets.union(thisValues, otherValues);
if(!isConsistentSet(union)) {
return false;
}
}
return true;
}
@Override
public String toString()
{
return "[equivalences=" + equivalences + ", keyToValue="
+ keyToValue + "]";
}
}