Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 443   Methods: 63
NCLOC: 271   Classes: 5
 
 Source file Conditionals Statements Methods TOTAL
ConcreteRelationIndex.java 50% 79.8% 68.3% 72.2%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2    *
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.collect;
 36   
 37    import java.io.Serializable;
 38    import java.util.*;
 39    import java.lang.ref.WeakReference;
 40    import edu.rice.cs.plt.tuple.Pair;
 41    import edu.rice.cs.plt.tuple.Option;
 42    import edu.rice.cs.plt.lambda.Thunk;
 43    import edu.rice.cs.plt.iter.IterUtil;
 44   
 45    /**
 46    * <p>A RelationIndex implementation that maintains concrete data structures to index the contents of
 47    * the relation. To support mutation in <em>other</em> indices to reflect changes made directly to
 48    * sets and iterators produced by this index, clients should override the {@link #addToRelation},
 49    * {@link #removeFromRelation}, and {@link #clearRelation} methods.</p>
 50    *
 51    * <p>Keys must have a valid {@code hashCode()} implementation.</p>
 52    */
 53    public class ConcreteRelationIndex<K, V> implements RelationIndex<K, V>, Serializable {
 54    // hold on to (possibly-empty) sets as long as they are referenced, but discard them
 55    // if that's not the case
 56    private final Map<K, WeakReference<ValueSet>> _valueSets;
 57    private final Map<K, PredicateSet<V>> _nonEmptyValueSets;
 58    private final Thunk<? extends Set<V>> _setFactory;
 59    private int _size;
 60   
 61    /**
 62    * Create an empty ConcreteRelationIndex.
 63    * @param mapFactory Produces an empty map for mapping keys to sets of values. The mutability
 64    * of {@code keys()} corresponds to this map's {@code keySet()}'s mutability.
 65    * @param setFactory Produces an initial set to hold values associated with a key.
 66    */
 67  6 public ConcreteRelationIndex(Thunk<? extends Map<K, PredicateSet<V>>> mapFactory,
 68    Thunk<? extends Set<V>> setFactory) {
 69  6 _valueSets = new HashMap<K, WeakReference<ValueSet>>();
 70  6 _nonEmptyValueSets = mapFactory.value();
 71  6 _setFactory = setFactory;
 72  6 _size = 0;
 73    }
 74   
 75    /**
 76    * Checks that the given pair, which does not appear in this index, can be added to the relation.
 77    * Called when a client attempts to directly mutate a match set, but before any mutation has
 78    * occurred. If the add is not permitted, subclasses may throw an exception. By default, does
 79    * nothing.
 80    */
 81  0 protected void validateAdd(K key, V value) {}
 82   
 83    /**
 84    * Add the given entry to the relation. Called when a client directly mutates a match set, and
 85    * after this index has been mutated. By default, does nothing.
 86    */
 87  0 protected void addToRelation(K key, V value) {}
 88   
 89    /**
 90    * Checks that the given pair, which appears in this index, can be removed from the relation.
 91    * Called when a client attempts to directly mutate a match set, but before any mutation has
 92    * occurred. If the removal is not permitted, subclasses may throw an exception. By
 93    * default, does nothing.
 94    */
 95  0 protected void validateRemove(K key, V value) {}
 96   
 97    /**
 98    * Checks that the given key, which appears with at least one value in this index, can be removed
 99    * with all associated values from the relation. Called when a client attempts to directly mutate
 100    * the key set or a match set, but before any mutation has occurred. If the removal is not
 101    * permitted, subclasses may throw an exception. By default, does nothing.
 102    */
 103  0 protected void validateRemoveKey(K key, PredicateSet<V> vals) {}
 104   
 105    /**
 106    * Remove the given entry from the relation. Called when a client directly mutates the key set or a
 107    * match set, and after this index has been mutated. By default, does nothing.
 108    */
 109  0 protected void removeFromRelation(K key, V value) {}
 110   
 111    /**
 112    * Checks that the relation can be cleared. Called when a client attempts to directly clear the key
 113    * set, but before any mutation has occurred. If clearing is not permitted, subclasses may throw
 114    * an exception. By default, does nothing.
 115    */
 116  0 protected void validateClear() {}
 117   
 118    /**
 119    * Clear the relation. Called when a client directly clears the key set, and after this index has
 120    * been mutated. By default, does nothing.
 121    */
 122  0 protected void clearRelation() {}
 123   
 124  24 public boolean contains(Object key, Object value) {
 125  24 PredicateSet<V> vals = _nonEmptyValueSets.get(key);
 126  24 return (vals != null) && vals.contains(value);
 127    }
 128   
 129  113 public PredicateSet<K> keys() { return new KeySet(); }
 130  62 public PredicateSet<V> match(K key) { return findMatch(key); }
 131  109 public Iterator<Pair<K, V>> iterator() { return new EntryIterator(); }
 132   
 133  25 public boolean isEmpty() { return _nonEmptyValueSets.isEmpty(); }
 134  51 public int size() { return _size; }
 135  0 public int size(int bound) { return _size < bound ? _size : bound; }
 136  0 public boolean isInfinite() { return false; }
 137  0 public boolean hasFixedSize() { return false; }
 138  0 public boolean isStatic() { return false; }
 139   
 140  31 public void added(K key, V value) { findMatch(key).doAdd(value); }
 141  1 public void removed(K key, V value) { findMatch(key).doRemove(value); }
 142   
 143  4 public void cleared() {
 144  4 for (K key : _nonEmptyValueSets.keySet()) {
 145    // the ValueSet is in _nonEmptyValueSets, so the reference must be defined
 146  9 _valueSets.get(key).get().doClear(false);
 147    }
 148  4 _nonEmptyValueSets.clear();
 149    }
 150   
 151    /**
 152    * Implementation of match. Allows subclasses to override match while still making this
 153    * functionality available to internal opeerations.
 154    */
 155  94 private ValueSet findMatch(K key) {
 156  94 WeakReference<ValueSet> ref = _valueSets.get(key);
 157  94 if (ref != null) {
 158  78 ValueSet result = ref.get();
 159  78 if (result != null) { return result; }
 160    }
 161  16 return new ValueSet(key);
 162    }
 163   
 164    /**
 165    * Supports removal by clearing the mapped-to value sets after delegating to the wrapped set's
 166    * mutation methods. Clients can prevent mutation by providing a map factory that produces
 167    * maps with immutable key sets.
 168    */
 169    private class KeySet extends DelegatingSet<K> {
 170  113 public KeySet() { super(_nonEmptyValueSets.keySet()); }
 171   
 172  0 @Override public boolean add(K obj) { throw new UnsupportedOperationException(); }
 173  0 @Override public boolean addAll(Collection<? extends K> c) {
 174  0 throw new UnsupportedOperationException();
 175    }
 176   
 177  47 @Override public Iterator<K> iterator() {
 178  47 return new Iterator<K>() {
 179    final Iterator<K> _i = _delegate.iterator();
 180    K _last = null;
 181  117 public boolean hasNext() { return _i.hasNext(); }
 182  72 public K next() { _last = _i.next(); return _last; }
 183  4 public void remove() {
 184  0 if (_last == null) { throw new IllegalStateException(); }
 185    // get a reference in order to prevent garbage collection
 186  4 ValueSet vals = _valueSets.get(_last).get();
 187  4 validateRemoveKey(_last, new ImmutableSet<V>(vals));
 188  4 _i.remove();
 189  4 vals.clearAndNotifyAfterMapRemoval();
 190    }
 191    };
 192    }
 193   
 194  1 @Override public boolean remove(Object o) {
 195  1 Option<K> cast = CollectUtil.castIfContains(this, o);
 196  1 if (cast.isSome()) {
 197  1 K key = cast.unwrap();
 198    // get a reference in order to prevent garbage collection
 199  1 ValueSet vals = _valueSets.get(key).get();
 200  1 validateRemoveKey(key, new ImmutableSet<V>(vals));
 201  1 _delegate.remove(key);
 202  1 vals.clearAndNotifyAfterMapRemoval();
 203  1 return true;
 204    }
 205  0 else { return false; }
 206    }
 207   
 208  1 @Override public void clear() {
 209  1 validateClear();
 210    // get references to ValueSets to prevent garbage collection
 211  1 List<ValueSet> toClear = new ArrayList<ValueSet>(_delegate.size());
 212  1 for (K key : _delegate) { toClear.add(_valueSets.get(key).get()); }
 213    // attempt clear before performing any other mutation (it might fail)
 214  1 _delegate.clear();
 215  1 for (ValueSet v : toClear) { v.clearAfterMapRemoval(); }
 216    // notify in one operation, rather than for each element:
 217  1 clearRelation();
 218    }
 219   
 220  1 @Override public boolean removeAll(Collection<?> c) {
 221  1 return abstractCollectionRemoveAll(c);
 222    }
 223   
 224  0 @Override public boolean retainAll(Collection<?> c) {
 225  0 return abstractCollectionRetainAll(c);
 226    }
 227    }
 228   
 229    /**
 230    * Adds/removes itself from the non-empty map as it changes, and removes the emptyMap entry
 231    * when garbage-collected. Notifies the relation after changes are made.
 232    */
 233    private class ValueSet extends DelegatingSet<V> implements Serializable {
 234    private final K _key;
 235    private int _size;
 236   
 237  16 public ValueSet(K key) {
 238  16 super(_setFactory.value());
 239  16 _key = key;
 240  16 _size = _delegate.size();
 241  16 _valueSets.put(key, new WeakReference<ValueSet>(this));
 242    // though unusual, the set factory might produce non-empty sets:
 243  0 if (!_delegate.isEmpty()) { _nonEmptyValueSets.put(_key, this); }
 244    }
 245   
 246  0 @Override protected void finalize() throws Throwable {
 247  0 try { _valueSets.remove(_key); }
 248  0 finally { super.finalize(); }
 249    }
 250   
 251  29 @Override public boolean isEmpty() { return _size == 0; }
 252  44 @Override public int size() { return _size; }
 253  0 @Override public int size(int bound) { return _size < bound ? _size : bound; }
 254   
 255  174 @Override public ValueSetIterator iterator() { return new ValueSetIterator(); }
 256   
 257  3 @Override public boolean add(V val) {
 258  3 boolean result = !_delegate.contains(val);
 259  3 if (result) {
 260  3 validateAdd(_key, val);
 261  3 _delegate.add(val);
 262  3 finishAdd();
 263  3 addToRelation(_key, val);
 264    }
 265  3 return result;
 266    }
 267   
 268  1 @Override public boolean remove(Object o) {
 269  1 Option<V> cast = CollectUtil.castIfContains(_delegate, o);
 270  1 if (cast.isSome()) {
 271  1 V val = cast.unwrap();
 272  1 validateRemove(_key, val);
 273  1 _delegate.remove(val);
 274  1 finishRemove();
 275  1 removeFromRelation(_key, val);
 276  1 return true;
 277    }
 278  0 else { return false; }
 279    }
 280   
 281  1 @Override public void clear() {
 282  1 if (_size != 0) {
 283  1 validateRemoveKey(_key, new ImmutableSet<V>(this));
 284  1 Iterable<V> vals = IterUtil.snapshot(this);
 285  1 _delegate.clear();
 286  1 finishClear(true);
 287  1 for (V val : vals) { removeFromRelation(_key, val); }
 288    }
 289    }
 290   
 291  1 @Override public boolean addAll(Collection<? extends V> c) {
 292  1 return abstractCollectionAddAll(c);
 293    }
 294  0 @Override public boolean retainAll(Collection<?> c) {
 295  0 return abstractCollectionRetainAll(c);
 296    }
 297  1 @Override public boolean removeAll(Collection<?> c) {
 298  1 return abstractCollectionRemoveAll(c);
 299    }
 300   
 301    /** Clear the set and notify the relation of the change. nonEmptyValueSets is already updated. */
 302  5 public void clearAndNotifyAfterMapRemoval() {
 303  5 if (_size != 0) {
 304  5 Iterable<V> vals = IterUtil.snapshot(this);
 305  5 try { _delegate.clear(); }
 306    catch (RuntimeException e) {
 307    // couldn't successfully clear
 308  0 _nonEmptyValueSets.put(_key, this);
 309  0 throw e;
 310    }
 311  5 finishClear(false);
 312  6 for (V val : vals) { removeFromRelation(_key, val); }
 313    }
 314    }
 315   
 316    /** Clear the set. Do not notify the relation. nonEmptyValueSets is already updated. */
 317  1 public void clearAfterMapRemoval() {
 318  1 if (_size != 0) {
 319  1 try { _delegate.clear(); }
 320    catch (RuntimeException e) {
 321    // couldn't successfully clear
 322  0 _nonEmptyValueSets.put(_key, this);
 323  0 throw e;
 324    }
 325  1 finishClear(false);
 326    }
 327    }
 328   
 329    /** Add an element without notifying the relation. Automatically update {@code nonEmptyValueSets}. */
 330  31 public void doAdd(V val) {
 331  31 boolean changed = _delegate.add(val);
 332    // changed should always be true, but we'll verify as a sanity check
 333  31 if (changed) { finishAdd(); }
 334    }
 335   
 336    /** Remove an element without notifying the relation. Automatically update {@code nonEmptyValueSets}. */
 337  1 public void doRemove(V val) {
 338  1 boolean changed = _delegate.remove(val);
 339    // changed should always be true, but we'll verify as a sanity check
 340  1 if (changed) { finishRemove(); }
 341    }
 342   
 343    /** Clear the set without notifying the relation. Update {@code nonEmptyValueSets} iff the flag is true. */
 344  9 public void doClear(boolean removeFromMap) {
 345  9 if (_size != 0) {
 346  9 _delegate.clear();
 347  9 finishClear(removeFromMap);
 348    }
 349    }
 350   
 351    /** Bookkeeping after an add mutates the set. */
 352  34 private void finishAdd() {
 353  19 if (_size == 0) { _nonEmptyValueSets.put(_key, this); }
 354  34 _size++;
 355  34 ConcreteRelationIndex.this._size++;
 356    }
 357   
 358    /** Bookkeeping after a remove mutates the set. */
 359  5 private void finishRemove() {
 360  5 _size--;
 361  5 ConcreteRelationIndex.this._size--;
 362  1 if (_size == 0) { _nonEmptyValueSets.remove(_key); }
 363    }
 364   
 365    /**
 366    * Bookkeeping after a remove mutates the set.
 367    * @param nonEmptyMapIterator An iterator that can be used to remove this set from nonEmptyValueSets.
 368    */
 369  0 private void finishRemove(Iterator<Map.Entry<K, PredicateSet<V>>> nonEmptyMapIterator) {
 370  0 _size--;
 371  0 ConcreteRelationIndex.this._size--;
 372  0 if (_size == 0) { nonEmptyMapIterator.remove(); }
 373    }
 374   
 375    /** Bookkeeping after a clear mutates the set. */
 376  16 private void finishClear(boolean removeFromMap) {
 377  16 ConcreteRelationIndex.this._size -= _size;
 378  16 _size = 0;
 379  1 if (removeFromMap) { _nonEmptyValueSets.remove(_key); }
 380    }
 381   
 382    /** An iterator that supports removal, and allows a nonEmptyMapIterator removal parameter. */
 383    public final class ValueSetIterator implements Iterator<V> {
 384    private final Iterator<? extends V> _i;
 385    private V _last; // null before iteration starts
 386  174 public ValueSetIterator() { _i = _delegate.iterator(); _last = null; }
 387  546 public boolean hasNext() { return _i.hasNext(); }
 388  307 public V next() { _last = _i.next(); return _last; }
 389  3 public void remove() {
 390  0 if (_last == null) { throw new IllegalStateException(); }
 391  3 validateRemove(_key, _last);
 392  3 _i.remove();
 393  3 finishRemove();
 394  3 removeFromRelation(_key, _last);
 395    }
 396  0 public void remove(Iterator<Map.Entry<K, PredicateSet<V>>> nonEmptyMapIterator) {
 397  0 if (_last == null) { throw new IllegalStateException(); }
 398  0 validateRemove(_key, _last);
 399  0 _i.remove();
 400  0 finishRemove(nonEmptyMapIterator);
 401  0 removeFromRelation(_key, _last);
 402    }
 403    }
 404    }
 405   
 406    private class EntryIterator implements Iterator<Pair<K, V>> {
 407    private final Iterator<Map.Entry<K, PredicateSet<V>>> _entries;
 408    private K _currentKey;
 409    private PredicateSet<V> _currentValues;
 410    private Iterator<V> _valuesIter;
 411   
 412  109 public EntryIterator() {
 413  109 _entries = _nonEmptyValueSets.entrySet().iterator();
 414  109 _currentKey = null;
 415  109 _currentValues = null;
 416  109 _valuesIter = null;
 417    }
 418   
 419  387 public boolean hasNext() {
 420  387 return (_valuesIter != null && _valuesIter.hasNext()) || _entries.hasNext();
 421    }
 422   
 423  214 public Pair<K, V> next() {
 424  214 if (_valuesIter == null || !_valuesIter.hasNext()) {
 425  122 Map.Entry<K, PredicateSet<V>> entry = _entries.next();
 426  122 _currentKey = entry.getKey();
 427  122 _currentValues = entry.getValue();
 428  122 _valuesIter = _currentValues.iterator();
 429    }
 430  214 return new Pair<K, V>(_currentKey, _valuesIter.next());
 431    }
 432   
 433  0 public void remove() {
 434    // javac 5 has a bug which rejects this instanceof type:
 435    //if (_valuesIter instanceof ConcreteRelationIndex<?, ?>.ValueSet.ValueSetIterator) {
 436  0 if (_valuesIter instanceof ConcreteRelationIndex.ValueSet.ValueSetIterator) {
 437  0 ((ValueSet.ValueSetIterator) _valuesIter).remove(_entries);
 438    }
 439  0 else { throw new UnsupportedOperationException(); }
 440    }
 441    }
 442   
 443    }