package org.aksw.sparqlify.database; import java.util.AbstractSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import com.google.common.collect.Sets; /** * IMPORTANT: If you are working with a child set, then do not modify the parent, * or things might go out of sync. * So the parent must remain unchanged while working with a child set * * @author Claus Stadler <cstadler@informatik.uni-leipzig.de> * * @param <T> */ public class NestedSet<T> extends AbstractSet<T> { private Set<T> parent = null; // if true, effective items is a view based on the parent't effective clauses // if false, effective items is a copy on its own behalf (independent of the parent) public boolean isView() { return addedItems != null; } int parentEffectiveItemCount; int cachedSize; private Set<T> effectiveItems; private Set<T> addedItems; private Set<T> removedItems; public NestedSet(Set<T> items, boolean asView) { if(asView) { this.addedItems = new HashSet<T>(); this.removedItems = new HashSet<T>(); this.parentEffectiveItemCount = items.size(); this.cachedSize = parentEffectiveItemCount; this.effectiveItems = Sets.union(addedItems, Sets.difference(items, removedItems)); } else { if(items == null) { this.effectiveItems = new HashSet<T>(); } else { this.effectiveItems = items; this.cachedSize = effectiveItems.size(); } } } private void checkCopy(Set<T> deltaSet) { if(deltaSet.size() > 4 * parentEffectiveItemCount) { this.effectiveItems = new HashSet<T>(effectiveItems); this.addedItems = null; this.removedItems = null; } } /* @Override public boolean addAll(Collection<? extends T> items) { boolean result = false; for(T item : items) { result = result || add(item); } return result; }*/ public boolean add(T item) { if(effectiveItems.contains(item)) { return false; } if(isView()) { checkCopy(addedItems); if(isView()) { addedItems.add(item); removedItems.remove(item); } } if(!isView()){ effectiveItems.add(item); } ++cachedSize; onAdd(item); return true; } /** * State a clause as removed - equivalent to stating the clause as unsatisfiable * * @param clause */ public boolean remove(Object item) { if(!effectiveItems.contains(item)) { return false; } if(isView()) { checkCopy(removedItems); if(isView()) { removedItems.add((T)item); addedItems.remove(item); } } if(!isView()) { effectiveItems.remove(item); } onRemove((T)item); --cachedSize; return true; } protected void onAdd(T item) { } protected void onRemove(T item) { } public int getNestingDepth() { if(parent != null && parent instanceof NestedSet) { return 1 + ((NestedSet<T>)parent).getNestingDepth(); } return 0; } @Override public boolean contains(Object item) { return effectiveItems.contains(item); } /** * Whether the given item is present in *this* instance, rather * than being actually a member of one of the parents * * @param item * @return */ //@Override public boolean containsDirect(Object item) { return isView() ? addedItems.contains(item) :effectiveItems.contains(item); } @Override public Iterator<T> iterator() { return effectiveItems.iterator(); } @Override public int size() { return cachedSize; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((effectiveItems == null) ? 0 : effectiveItems.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; NestedSet other = (NestedSet) obj; if (effectiveItems == null) { if (other.effectiveItems != null) return false; } else if (!effectiveItems.equals(other.effectiveItems)) return false; return true; } }