package checkers.flow;
import java.util.*;
import javax.lang.model.element.AnnotationMirror;
/**
* Maintains multiple gen-kill sets, "keyed" by a value. For instance, the
* flow-sensitive inference implemented in {@link Flow} uses a
* {@link GenKillBits} keyed by {@link AnnotationMirror}s to simultaneously
* track the gen-kill sets of multiple type qualifiers.
*
* <p>
*
* This class is essentially an abstraction for maintaining and combining
* multiple simultaneous bit vectors.
*
* @param <K> the type of the key
*/
public class GenKillBits<K> {
private final Map<K, BitSet> bitsets;
/**
* Creates a new {@link GenKillBits} that is a deep copy of the
* {@link GenKillBits} passed as an argument.
*
* @param <K> the key type of the group
* @param other the {@link GenKillBits} to copy
* @return a deep copy of the contents of {@code g}
*
* @see GenKillBits#GenKillBits(GenKillBits)
*/
public static <K> GenKillBits<K> copy(GenKillBits<K> other) {
if (other == null)
return null;
else
return new GenKillBits<K>(other);
}
/**
* Creates a new {@link GenKillBits} with the specified set of keys.
* (Once specified, the keys may not be changed.)
*
* @param keys the keys for the {@link GenKillBits}
*/
public GenKillBits(Collection<K> keys) {
bitsets = new HashMap<K, BitSet>(keys.size());
for (K key : keys)
bitsets.put(key, new BitSet());
}
/**
* Creates a new {@link GenKillBits} that is a deep copy of the
* {@link GenKillBits} passed as an argument.
*
* @param other the {@link GenKillBits} to copy
*
* @see GenKillBits#copy(GenKillBits)
*/
public GenKillBits(GenKillBits<K> other) {
bitsets = new HashMap<K, BitSet>(other.bitsets);
for (K key : bitsets.keySet()) {
BitSet newbits = (BitSet) bitsets.get(key).clone();
bitsets.put(key, newbits);
}
}
/**
* Sets the bit (gen) for the key at the specified index.
*
* @param key the key for which the bit should be set
* @param index the index at which to set the bit
* @throws IllegalArgumentException if the key is not one of the keys for
* this group
*/
public void set(K key, int index) {
if (!bitsets.containsKey(key))
throw new IllegalArgumentException();
bitsets.get(key).set(index);
}
/**
* Retrieves the bit for the key at the specified index.
*
* @param key
* @param index
* @return the value of the bit for the key at the index
* @throws IllegalArgumentException if the key is not one of the keys for
* this group
*/
public boolean get(K key, int index) {
if (!bitsets.containsKey(key))
throw new IllegalArgumentException();
return bitsets.get(key).get(index);
}
/**
* Clears the bit (kill) for the key at the specified index.
*
* @param key the key for which the bit should be set
* @param index the index at which to set the bit
* @throws IllegalArgumentException if the key is not one of the keys for
* this group
*/
public void clear(K key, int index) {
if (!bitsets.containsKey(key))
throw new IllegalArgumentException();
bitsets.get(key).clear(index);
}
/**
* Merges each gen-kill set in this group with the one corresponding to the
* same key in {@code other} via boolean "and" on each bit. Modifies this
* gen-kill set.
*
* @param other the group to "and" with
* @throws IllegalArgumentException if the other group is missing a key from
* this group
*/
public void and(GenKillBits<K> other) {
for (K key : bitsets.keySet()) {
if (!other.bitsets.containsKey(key))
throw new IllegalArgumentException();
bitsets.get(key).and(other.bitsets.get(key));
}
}
/**
* Merges each gen-kill set in this group with the one corresponding to the
* same key in {@code other} via boolean "or" on each bit. Modifies this
* gen-kill set.
*
* @param other the group to "or" with
* @throws IllegalArgumentException if the other group is missing a key from
* this group
*/
public void or(GenKillBits<K> other) {
for (K key : bitsets.keySet()) {
if (!other.bitsets.containsKey(key))
throw new IllegalArgumentException();
bitsets.get(key).or(other.bitsets.get(key));
}
}
public String toString() {
return "[GenKill: " + bitsets + "]";
}
}