Copy pcollections's HashPMap to kotlin/reflect/jvm/internal

It will be used for caching KClass instances for foreign (Java) classes
This commit is contained in:
Alexander Udalov
2014-06-09 21:24:27 +04:00
parent 89d6f25fb6
commit 58bc611e3a
12 changed files with 1291 additions and 0 deletions
@@ -36,6 +36,8 @@ public class JetCodeConformanceTest extends TestCase {
private static final Pattern SOURCES_FILE_PATTERN = Pattern.compile("(.+\\.java|.+\\.kt|.+\\.js)");
private static final List<File> EXCLUDED_FILES_AND_DIRS = Arrays.asList(
new File("android.tests.dependencies"),
new File("core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections"),
new File("libraries/tools/runtime/target/copied-sources"),
new File("dependencies"),
new File("examples"),
new File("js/js.translator/qunit/qunit.js"),
@@ -0,0 +1,210 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.AbstractSequentialList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
/**
*
* A simple persistent stack of non-null values.
* <p>
* This implementation is thread-safe (assuming Java's AbstractSequentialList is thread-safe),
* although its iterators may not be.
*
* @author harold
*
* @param <E>
*/
public final class ConsPStack<E> extends AbstractSequentialList<E> implements PStack<E> {
//// STATIC FACTORY METHODS ////
private static final ConsPStack<Object> EMPTY = new ConsPStack<Object>();
/**
* @param <E>
* @return an empty stack
*/
@SuppressWarnings("unchecked")
public static <E> ConsPStack<E> empty() {
return (ConsPStack<E>)EMPTY; }
/**
* @param <E>
* @param e
* @return empty().plus(e)
*/
public static <E> ConsPStack<E> singleton(final E e) {
return ConsPStack.<E>empty().plus(e); }
/**
* @param <E>
* @param list
* @return a stack consisting of the elements of list in the order of list.iterator()
*/
@SuppressWarnings("unchecked")
public static <E> ConsPStack<E> from(final Collection<? extends E> list) {
if(list instanceof ConsPStack)
return (ConsPStack<E>)list; //(actually we only know it's ConsPStack<? extends E>)
// but that's good enough for an immutable
// (i.e. we can't mess someone else up by adding the wrong type to it)
return from(list.iterator());
}
private static <E> ConsPStack<E> from(final Iterator<? extends E> i) {
if(!i.hasNext()) return empty();
E e = i.next();
return from(i).plus(e);
}
//// PRIVATE CONSTRUCTORS ////
private final E first; private final ConsPStack<E> rest;
private final int size;
// not externally instantiable (or subclassable):
private ConsPStack() { // EMPTY constructor
if(EMPTY!=null)
throw new RuntimeException("empty constructor should only be used once");
size = 0; first=null; rest=null;
}
private ConsPStack(final E first, final ConsPStack<E> rest) {
this.first = first; this.rest = rest;
size = 1 + rest.size;
}
//// REQUIRED METHODS FROM AbstractSequentialList ////
@Override
public int size() {
return size; }
@Override
public ListIterator<E> listIterator(final int index) {
if(index<0 || index>size) throw new IndexOutOfBoundsException();
return new ListIterator<E>() {
int i = index;
ConsPStack<E> next = subList(index);
public boolean hasNext() {
return next.size>0; }
public boolean hasPrevious() {
return i>0; }
public int nextIndex() {
return index; }
public int previousIndex() {
return index-1; }
public E next() {
E e = next.first;
next = next.rest;
return e;
}
public E previous() {
System.err.println("ConsPStack.listIterator().previous() is inefficient, don't use it!");
next = subList(index-1); // go from beginning...
return next.first;
}
public void add(final E o) {
throw new UnsupportedOperationException(); }
public void remove() {
throw new UnsupportedOperationException(); }
public void set(final E o) {
throw new UnsupportedOperationException(); }
};
}
//// OVERRIDDEN METHODS FROM AbstractSequentialList ////
@Override
public ConsPStack<E> subList(final int start, final int end) {
if(start<0 || end>size || start>end)
throw new IndexOutOfBoundsException();
if(end==size) // want a substack
return subList(start); // this is faster
if(start==end) // want nothing
return empty();
if(start==0) // want the current element
return new ConsPStack<E>(first, rest.subList(0, end-1));
// otherwise, don't want the current element:
return rest.subList(start-1, end-1);
}
//// IMPLEMENTED METHODS OF PStack ////
public ConsPStack<E> plus(final E e) {
return new ConsPStack<E>(e, this);
}
public ConsPStack<E> plusAll(final Collection<? extends E> list) {
ConsPStack<E> result = this;
for(E e : list)
result = result.plus(e);
return result;
}
public ConsPStack<E> plus(final int i, final E e) {
if(i<0 || i>size)
throw new IndexOutOfBoundsException();
if(i==0) // insert at beginning
return plus(e);
return new ConsPStack<E>(first, rest.plus(i-1, e));
}
public ConsPStack<E> plusAll(final int i, final Collection<? extends E> list) {
// TODO inefficient if list.isEmpty()
if(i<0 || i>size)
throw new IndexOutOfBoundsException();
if(i==0)
return plusAll(list);
return new ConsPStack<E>(first, rest.plusAll(i-1, list));
}
public ConsPStack<E> minus(final Object e) {
if(size==0)
return this;
if(first.equals(e)) // found it
return rest; // don't recurse (only remove one)
// otherwise keep looking:
ConsPStack<E> newRest = rest.minus(e);
if(newRest==rest) return this;
return new ConsPStack<E>(first, newRest);
}
public ConsPStack<E> minus(final int i) {
return minus(get(i));
}
public ConsPStack<E> minusAll(final Collection<?> list) {
if(size==0)
return this;
if(list.contains(first)) // get rid of current element
return rest.minusAll(list); // recursively delete all
// either way keep looking:
ConsPStack<E> newRest = rest.minusAll(list);
if(newRest==rest) return this;
return new ConsPStack<E>(first, newRest);
}
public ConsPStack<E> with(final int i, final E e) {
if(i<0 || i>=size)
throw new IndexOutOfBoundsException();
if(i==0) {
if(first.equals(e)) return this;
return new ConsPStack<E>(e, rest);
}
ConsPStack<E> newRest = rest.with(i-1, e);
if(newRest==rest) return this;
return new ConsPStack<E>(first, newRest);
}
public ConsPStack<E> subList(final int start) {
if(start<0 || start>size)
throw new IndexOutOfBoundsException();
if(start==0)
return this;
return rest.subList(start-1);
}
}
@@ -0,0 +1,175 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
*
* A persistent map from non-null keys to non-null values.
* <p>
* This map uses a given integer map to map hashcodes to lists of elements
* with the same hashcode. Thus if all elements have the same hashcode, performance
* is reduced to that of an association list.
* <p>
* This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe),
* although its iterators may not be.
*
* @author harold
*
* @param <K>
* @param <V>
*/
public final class HashPMap<K,V> extends AbstractMap<K,V> implements PMap<K,V> {
//// STATIC FACTORY METHODS ////
/**
* @param <K>
* @param <V>
* @param intMap
* @return a map backed by an empty version of intMap,
* i.e. backed by intMap.minusAll(intMap.keySet())
*/
public static <K,V> HashPMap<K,V> empty(final PMap<Integer,PSequence<Entry<K,V>>> intMap) {
return new HashPMap<K,V>(intMap.minusAll(intMap.keySet()), 0); }
//// PRIVATE CONSTRUCTORS ////
private final PMap<Integer,PSequence<Entry<K,V>>> intMap;
private final int size;
// not externally instantiable (or subclassable):
private HashPMap(final PMap<Integer,PSequence<Entry<K,V>>> intMap, final int size) {
this.intMap = intMap; this.size = size; }
//// REQUIRED METHODS FROM AbstractMap ////
// this cache variable is thread-safe since assignment in Java is atomic:
private Set<Entry<K,V>> entrySet = null;
@Override
public Set<Entry<K,V>> entrySet() {
if(entrySet==null)
entrySet = new AbstractSet<Entry<K,V>>() {
// REQUIRED METHODS OF AbstractSet //
@Override
public int size() {
return size; }
@Override
public Iterator<Entry<K,V>> iterator() {
return new SequenceIterator<Entry<K,V>>(intMap.values().iterator()); }
// OVERRIDDEN METHODS OF AbstractSet //
@Override
public boolean contains(final Object e) {
if(!(e instanceof Entry))
return false;
V value = get(((Entry<?,?>)e).getKey());
return value!=null && value.equals(((Entry<?,?>)e).getValue());
}
};
return entrySet;
}
//// OVERRIDDEN METHODS FROM AbstractMap ////
@Override
public int size() {
return size; }
@Override
public boolean containsKey(final Object key) {
return keyIndexIn(getEntries(key.hashCode()), key) != -1; }
@Override
public V get(final Object key) {
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
for(Entry<K,V> entry : entries)
if(entry.getKey().equals(key))
return entry.getValue();
return null;
}
//// IMPLEMENTED METHODS OF PMap////
public HashPMap<K,V> plusAll(final Map<? extends K, ? extends V> map) {
HashPMap<K,V> result = this;
for(Entry<? extends K,? extends V> entry : map.entrySet())
result = result.plus(entry.getKey(), entry.getValue());
return result;
}
public HashPMap<K,V> minusAll(final Collection<?> keys) {
HashPMap<K,V> result = this;
for(Object key : keys)
result = result.minus(key);
return result;
}
public HashPMap<K,V> plus(final K key, final V value) {
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
int size0 = entries.size(),
i = keyIndexIn(entries, key);
if(i!=-1) entries = entries.minus(i);
entries = entries.plus(new SimpleImmutableEntry<K,V>(key, value));
return new HashPMap<K,V>(intMap.plus(key.hashCode(), entries),
size-size0+entries.size());
}
public HashPMap<K,V> minus(final Object key) {
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
int i = keyIndexIn(entries, key);
if(i==-1) // key not in this
return this;
entries = entries.minus(i);
if(entries.size()==0) // get rid of the entire hash entry
return new HashPMap<K,V>(intMap.minus(key.hashCode()),
size-1);
// otherwise replace hash entry with new smaller one:
return new HashPMap<K,V>(intMap.plus(key.hashCode(), entries),
size-1);
}
//// PRIVATE UTILITIES ////
private PSequence<Entry<K,V>> getEntries(final int hash) {
PSequence<Entry<K,V>> entries = intMap.get(hash);
if(entries==null) return ConsPStack.empty();
return entries;
}
//// PRIVATE STATIC UTILITIES ////
private static <K,V> int keyIndexIn(final PSequence<Entry<K,V>> entries, final Object key) {
int i=0;
for(Entry<K,V> entry : entries) {
if(entry.getKey().equals(key))
return i;
i++;
}
return -1;
}
static class SequenceIterator<E> implements Iterator<E> {
private final Iterator<PSequence<E>> i;
private PSequence<E> seq = ConsPStack.empty();
SequenceIterator(Iterator<PSequence<E>> i) {
this.i = i; }
public boolean hasNext() {
return seq.size()>0 || i.hasNext(); }
public E next() {
if(seq.size()==0)
seq = i.next();
final E result = seq.get(0);
seq = seq.subList(1, seq.size());
return result;
}
public void remove() {
throw new UnsupportedOperationException(); }
}
}
@@ -0,0 +1,51 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Map;
import java.util.Map.Entry;
/**
*
* A static convenience class for creating efficient persistent maps.
* <p>
* This class simply creates HashPMaps backed by IntTreePMaps.
*
* @author harold
*/
public final class HashTreePMap {
// not instantiable (or subclassable):
private HashTreePMap() {}
private static final HashPMap<Object,Object> EMPTY
= HashPMap.empty(IntTreePMap.<PSequence<Entry<Object,Object>>>empty());
/**
* @param <K>
* @param <V>
* @return an empty map
*/
@SuppressWarnings("unchecked")
public static <K,V> HashPMap<K,V> empty() {
return (HashPMap<K,V>)EMPTY; }
/**
* @param <K>
* @param <V>
* @param key
* @param value
* @return empty().plus(key, value)
*/
public static <K,V> HashPMap<K,V> singleton(final K key, final V value) {
return HashTreePMap.<K,V>empty().plus(key, value); }
/**
* @param <K>
* @param <V>
* @param map
* @return empty().plusAll(map)
*/
public static <K,V> HashPMap<K,V> from(final Map<? extends K, ? extends V> map) {
return HashTreePMap.<K,V>empty().plusAll(map); }
}
@@ -0,0 +1,309 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Iterator;
import java.util.Map.Entry;
/**
*
* A non-public utility class for persistent balanced tree maps with integer keys.
* <p>
* To allow for efficiently increasing all keys above a certain value or decreasing
* all keys below a certain value, the keys values are stored relative to their parent.
* This makes this map a good backing for fast insertion and removal of indices in a
* vector.
* <p>
* This implementation is thread-safe except for its iterators.
* <p>
* Other than that, this tree is based on the Glasgow Haskell Compiler's Data.Map implementation,
* which in turn is based on "size balanced binary trees" as described by:
* <p>
* Stephen Adams, "Efficient sets: a balancing act",
* Journal of Functional Programming 3(4):553-562, October 1993,
* http://www.swiss.ai.mit.edu/~adams/BB/.
* <p>
* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance",
* SIAM journal of computing 2(1), March 1973.
*
* @author harold
*
* @param <V>
*/
class IntTree<V> {
// marker value:
static final IntTree<Object> EMPTYNODE = new IntTree<Object>();
private final long key; // we use longs so relative keys can express all ints
// (e.g. if this has key -10 and right has 'absolute' key MAXINT,
// then its relative key is MAXINT+10 which overflows)
// there might be some way to deal with this based on left-verse-right logic,
// but that sounds like a mess.
private final V value; // null value means this is empty node
private final IntTree<V> left, right;
private final int size;
private IntTree() {
if(EMPTYNODE!=null)
throw new RuntimeException("empty constructor should only be used once");
size = 0;
key=0; value=null; left=null; right=null;
}
private IntTree(final long key, final V value, final IntTree<V> left, final IntTree<V> right) {
this.key = key; this.value = value;
this.left = left; this.right = right;
size = 1 + left.size + right.size;
}
private IntTree<V> withKey(final long newKey) {
if(size==0 || newKey==key) return this;
return new IntTree<V>(newKey, value, left, right); }
Iterator<Entry<Integer,V>> iterator() {
return new EntryIterator<V>(this); }
int size() {
return size; }
boolean containsKey(final long key) {
if(size==0)
return false;
if(key < this.key)
return left.containsKey(key-this.key);
if(key > this.key)
return right.containsKey(key-this.key);
// otherwise key==this.key:
return true;
}
V get(final long key) {
if(size==0)
return null;
if(key < this.key)
return left.get(key-this.key);
if(key > this.key)
return right.get(key-this.key);
// otherwise key==this.key:
return value;
}
IntTree<V> plus(final long key, final V value) {
if(size==0)
return new IntTree<V>(key, value, this, this);
if(key < this.key)
return rebalanced(left.plus(key-this.key, value), right);
if(key > this.key)
return rebalanced(left, right.plus(key-this.key, value));
// otherwise key==this.key, so we simply replace this, with no effect on balance:
if(value==this.value)
return this;
return new IntTree<V>(key, value, left, right);
}
IntTree<V> minus(final long key) {
if(size==0)
return this;
if(key < this.key)
return rebalanced(left.minus(key-this.key), right);
if(key > this.key)
return rebalanced(left, right.minus(key-this.key));
// otherwise key==this.key, so we are killing this node:
if(left.size==0) // we can just become right node
// make key 'absolute':
return right.withKey(right.key+this.key);
if(right.size==0) // we can just become left node
return left.withKey(left.key+this.key);
// otherwise replace this with the next key (i.e. the smallest key to the right):
// TODO have minNode() instead of minKey to avoid having to call get()
// TODO get node from larger subtree, i.e. if left.size>right.size use left.maxNode()
// TODO have faster minusMin() instead of just using minus()
long newKey = right.minKey() + this.key;
//(right.minKey() is relative to this; adding this.key makes it 'absolute'
// where 'absolute' really means relative to the parent of this)
V newValue = right.get(newKey-this.key);
// now that we've got the new stuff, take it out of the right subtree:
IntTree<V> newRight = right.minus(newKey-this.key);
// lastly, make the subtree keys relative to newKey (currently they are relative to this.key):
newRight = newRight.withKey( (newRight.key+this.key) - newKey );
// left is definitely not empty:
IntTree<V> newLeft = left.withKey( (left.key+this.key) - newKey );
return rebalanced(newKey, newValue, newLeft, newRight);
}
/**
* Changes every key k>=key to k+delta.
*
* This method will create an _invalid_ tree if delta<0
* and the distance between the smallest k>=key in this
* and the largest j<key in this is |delta| or less.
*
* In other words, this method must not result in any change
* in the order of the keys in this, since the tree structure is
* not being changed at all.
*/
IntTree<V> changeKeysAbove(final long key, final int delta) {
if(size==0 || delta==0)
return this;
if(this.key>=key)
// adding delta to this.key changes the keys of _all_ children of this,
// so we now need to un-change the children of this smaller than key,
// all of which are to the left. note that we still use the 'old' relative key...:
return new IntTree<V>(this.key+delta, value, left.changeKeysBelow(key-this.key, -delta), right);
// otherwise, doesn't apply yet, look to the right:
IntTree<V> newRight = right.changeKeysAbove(key-this.key, delta);
if(newRight==right) return this;
return new IntTree<V>(this.key, value, left, newRight);
}
/**
* Changes every key k<key to k+delta.
*
* This method will create an _invalid_ tree if delta>0
* and the distance between the largest k<key in this
* and the smallest j>=key in this is delta or less.
*
* In other words, this method must not result in any overlap or change
* in the order of the keys in this, since the tree _structure_ is
* not being changed at all.
*/
IntTree<V> changeKeysBelow(final long key, final int delta) {
if(size==0 || delta==0)
return this;
if(this.key<key)
// adding delta to this.key changes the keys of _all_ children of this,
// so we now need to un-change the children of this larger than key,
// all of which are to the right. note that we still use the 'old' relative key...:
return new IntTree<V>(this.key+delta, value, left, right.changeKeysAbove(key-this.key, -delta));
// otherwise, doesn't apply yet, look to the left:
IntTree<V> newLeft = left.changeKeysBelow(key-this.key, delta);
if(newLeft==left) return this;
return new IntTree<V>(this.key, value, newLeft, right);
}
// min key in this:
private long minKey() {
if(left.size==0)
return key;
// make key 'absolute' (i.e. relative to the parent of this):
return left.minKey() + this.key;
}
private IntTree<V> rebalanced(final IntTree<V> newLeft, final IntTree<V> newRight) {
if(newLeft==left && newRight==right)
return this; // already balanced
return rebalanced(key, value, newLeft, newRight);
}
private static final int OMEGA = 5;
private static final int ALPHA = 2;
// rebalance a tree that is off-balance by at most 1:
private static <V> IntTree<V> rebalanced(final long key, final V value,
final IntTree<V> left, final IntTree<V> right) {
if(left.size + right.size > 1) {
if(left.size >= OMEGA*right.size) { // rotate to the right
IntTree<V> ll = left.left, lr = left.right;
if(lr.size < ALPHA*ll.size) // single rotation
return new IntTree<V>(left.key+key, left.value,
ll,
new IntTree<V>(-left.key, value,
lr.withKey(lr.key+left.key),
right));
else { // double rotation:
IntTree<V> lrl = lr.left, lrr = lr.right;
return new IntTree<V>(lr.key+left.key+key, lr.value,
new IntTree<V>(-lr.key, left.value,
ll,
lrl.withKey(lrl.key+lr.key)),
new IntTree<V>(-left.key-lr.key, value,
lrr.withKey(lrr.key+lr.key+left.key),
right));
}
}
else if(right.size >= OMEGA*left.size) { // rotate to the left
IntTree<V> rl = right.left, rr = right.right;
if(rl.size < ALPHA*rr.size) // single rotation
return new IntTree<V>(right.key+key, right.value,
new IntTree<V>(-right.key, value,
left,
rl.withKey(rl.key+right.key)),
rr);
else { // double rotation:
IntTree<V> rll = rl.left, rlr = rl.right;
return new IntTree<V>(rl.key+right.key+key, rl.value,
new IntTree<V>(-right.key-rl.key, value,
left,
rll.withKey(rll.key+rl.key+right.key)),
new IntTree<V>(-rl.key, right.value,
rlr.withKey(rlr.key+rl.key),
rr));
}
}
}
// otherwise already balanced enough:
return new IntTree<V>(key, value, left, right);
}
////entrySet().iterator() IMPLEMENTATION ////
// TODO make this a ListIterator?
private static final class EntryIterator<V> implements Iterator<Entry<Integer,V>> {
private PStack<IntTree<V>> stack = ConsPStack.empty(); //path of nonempty nodes
private int key = 0; // note we use _int_ here since this is a truly absolute key
EntryIterator(final IntTree<V> root) {
gotoMinOf(root); }
public boolean hasNext() {
return stack.size()>0; }
public Entry<Integer,V> next() {
IntTree<V> node = stack.get(0);
final Entry<Integer,V> result = new SimpleImmutableEntry<Integer,V>(key, node.value);
// find next node.
// we've already done everything smaller,
// so try least larger node:
if(node.right.size>0) // we can descend to the right
gotoMinOf(node.right);
else // can't descend to the right -- try ascending to the right
while (true) { // find current node's least larger ancestor, if any
key -= node.key; // revert to parent's key
stack = stack.subList(1); // climb up to parent
// if parent was larger than child or there was no parent, we're done:
if(node.key<0 || stack.size()==0)
break;
// otherwise parent was smaller -- try its parent:
node = stack.get(0);
}
return result;
}
public void remove() {
throw new UnsupportedOperationException(); }
// extend the stack to its least non-empty node:
private void gotoMinOf(IntTree<V> node) {
while(node.size>0) {
stack = stack.plus(node);
key += node.key;
node = node.left;
}
}
}
}
@@ -0,0 +1,163 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
*
* An efficient persistent map from integer keys to non-null values.
* <p>
* Iteration occurs in the integer order of the keys.
* <p>
* This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe),
* although its iterators may not be.
* <p>
* The balanced tree is based on the Glasgow Haskell Compiler's Data.Map implementation,
* which in turn is based on "size balanced binary trees" as described by:
* <p>
* Stephen Adams, "Efficient sets: a balancing act",
* Journal of Functional Programming 3(4):553-562, October 1993,
* http://www.swiss.ai.mit.edu/~adams/BB/.
* <p>
* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance",
* SIAM journal of computing 2(1), March 1973.
*
* @author harold
*
* @param <V>
*/
public final class IntTreePMap<V> extends AbstractMap<Integer,V> implements PMap<Integer,V> {
//// STATIC FACTORY METHODS ////
private static final IntTreePMap<Object> EMPTY = new IntTreePMap<Object>(IntTree.EMPTYNODE);
/**
* @param <V>
* @return an empty map
*/
@SuppressWarnings("unchecked")
public static <V> IntTreePMap<V> empty() {
return (IntTreePMap<V>)EMPTY; }
/**
* @param <V>
* @param key
* @param value
* @return empty().plus(key, value)
*/
public static <V> IntTreePMap<V> singleton(final Integer key, final V value) {
return IntTreePMap.<V>empty().plus(key, value); }
/**
* @param <V>
* @param map
* @return empty().plusAll(map)
*/
@SuppressWarnings("unchecked")
public static <V> IntTreePMap<V> from(final Map<? extends Integer, ? extends V> map) {
if(map instanceof IntTreePMap)
return (IntTreePMap<V>)map; //(actually we only know it's IntTreePMap<? extends V>)
// but that's good enough for an immutable
// (i.e. we can't mess someone else up by adding the wrong type to it)
return IntTreePMap.<V>empty().plusAll(map); }
//// PRIVATE CONSTRUCTORS ////
private final IntTree<V> root;
// not externally instantiable (or subclassable):
private IntTreePMap(final IntTree<V> root) {
this.root = root; }
private IntTreePMap<V> withRoot(final IntTree<V> root) {
if(root==this.root) return this;
return new IntTreePMap<V>(root); }
//// UNINHERITED METHODS OF IntTreePMap ////
IntTreePMap<V> withKeysChangedAbove(final int key, final int delta) {
// TODO check preconditions of changeKeysAbove()
// TODO make public?
return withRoot( root.changeKeysAbove(key, delta) );
}
IntTreePMap<V> withKeysChangedBelow(final int key, final int delta) {
// TODO check preconditions of changeKeysAbove()
// TODO make public?
return withRoot( root.changeKeysBelow(key, delta) );
}
//// REQUIRED METHODS FROM AbstractMap ////
// this cache variable is thread-safe, since assignment in Java is atomic:
private Set<Entry<Integer,V>> entrySet = null;
@Override
public Set<Entry<Integer,V>> entrySet() {
if(entrySet==null)
entrySet = new AbstractSet<Entry<Integer,V>>() {
// REQUIRED METHODS OF AbstractSet //
@Override
public int size() { // same as Map
return IntTreePMap.this.size(); }
@Override
public Iterator<Entry<Integer,V>> iterator() {
return root.iterator(); }
// OVERRIDDEN METHODS OF AbstractSet //
@Override
public boolean contains(final Object e) {
if(!(e instanceof Entry))
return false;
V value = get(((Entry<?,?>)e).getKey());
return value!=null && value.equals(((Entry<?,?>)e).getValue());
}
};
return entrySet;
}
//// OVERRIDDEN METHODS FROM AbstractMap ////
@Override
public int size() {
return root.size(); }
@Override
public boolean containsKey(final Object key) {
if(!(key instanceof Integer))
return false;
return root.containsKey((Integer)key);
}
@Override
public V get(final Object key) {
if(!(key instanceof Integer))
return null;
return root.get((Integer)key);
}
//// IMPLEMENTED METHODS OF PMap////
public IntTreePMap<V> plus(final Integer key, final V value) {
return withRoot( root.plus(key, value) ); }
public IntTreePMap<V> minus(final Object key) {
if(!(key instanceof Integer)) return this;
return withRoot( root.minus((Integer)key) ); }
public IntTreePMap<V> plusAll(final Map<? extends Integer, ? extends V> map) {
IntTree<V> root = this.root;
for(Entry<? extends Integer,? extends V> entry : map.entrySet())
root = root.plus(entry.getKey(), entry.getValue());
return withRoot(root);
}
public IntTreePMap<V> minusAll(final Collection<?> keys) {
IntTree<V> root = this.root;
for(Object key : keys)
if(key instanceof Integer)
root = root.minus((Integer)key);
return withRoot(root);
}
}
@@ -0,0 +1,47 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
/**
*
* An immutable, persistent collection of non-null elements of type E.
*
* @author harold
*
* @param <E>
*/
public interface PCollection<E> extends Collection<E> {
/**
* @param e non-null
* @return a collection which contains e and all of the elements of this
*/
public PCollection<E> plus(E e);
/**
* @param list contains no null elements
* @return a collection which contains all of the elements of list and this
*/
public PCollection<E> plusAll(Collection<? extends E> list);
/**
* @param e
* @return this with a single instance of e removed, if e is in this
*/
public PCollection<E> minus(Object e);
/**
* @param list
* @return this with all elements of list completely removed
*/
public PCollection<E> minusAll(Collection<?> list);
// TODO public PCollection<E> retainingAll(Collection<?> list);
@Deprecated boolean add(E o);
@Deprecated boolean remove(Object o);
@Deprecated boolean addAll(Collection<? extends E> c);
@Deprecated boolean removeAll(Collection<?> c);
@Deprecated boolean retainAll(Collection<?> c);
@Deprecated void clear();
}
@@ -0,0 +1,45 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
import java.util.Map;
/**
*
* An immutable, persistent map from non-null keys of type K to non-null values of type V.
*
* @author harold
*
* @param <K>
* @param <V>
*/
public interface PMap<K,V> extends Map<K,V> {
/**
* @param key non-null
* @param value non-null
* @return a map with the mappings of this but with key mapped to value
*/
public PMap<K,V> plus(K key, V value);
/**
* @param map
* @return this combined with map, with map's mappings used for any keys in both map and this
*/
public PMap<K,V> plusAll(Map<? extends K, ? extends V> map);
/**
* @param key
* @return a map with the mappings of this but with no value for key
*/
public PMap<K,V> minus(Object key);
/**
* @param keys
* @return a map with the mappings of this but with no value for any element of keys
*/
public PMap<K,V> minusAll(Collection<?> keys);
@Deprecated V put(K k, V v);
@Deprecated V remove(Object k);
@Deprecated void putAll(Map<? extends K, ? extends V> m);
@Deprecated void clear();
}
@@ -0,0 +1,69 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
import java.util.List;
/**
*
* An immutable, persistent indexed collection.
*
* @author harold
*
* @param <E>
*/
public interface PSequence<E> extends PCollection<E>, List<E> {
//@Override
public PSequence<E> plus(E e);
//@Override
public PSequence<E> plusAll(Collection<? extends E> list);
/**
* @param i
* @param e
* @return a sequence consisting of the elements of this with e replacing the element at index i.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;=this.size()
*/
public PSequence<E> with(int i, E e);
/**
* @param i
* @param e non-null
* @return a sequence consisting of the elements of this with e inserted at index i.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;this.size()
*/
public PSequence<E> plus(int i, E e);
/**
* @param i
* @param list
* @return a sequence consisting of the elements of this with list inserted at index i.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;this.size()
*/
public PSequence<E> plusAll(int i, Collection<? extends E> list);
/**
* Returns a sequence consisting of the elements of this without the first occurrence of e.
*/
//@Override
public PSequence<E> minus(Object e);
//@Override
public PSequence<E> minusAll(Collection<?> list);
/**
* @param i
* @return a sequence consisting of the elements of this with the element at index i removed.
* @throws IndexOutOfBOundsException if i&lt;0 || i&gt;=this.size()
*/
public PSequence<E> minus(int i);
//@Override
public PSequence<E> subList(int start, int end);
@Deprecated boolean addAll(int index, Collection<? extends E> c);
@Deprecated E set(int index, E element);
@Deprecated void add(int index, E element);
@Deprecated E remove(int index);
}
@@ -0,0 +1,53 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Collection;
/**
*
* An immutable, persistent stack.
*
* @author harold
*
* @param <E>
*/
public interface PStack<E> extends PSequence<E> {
/**
* Returns a stack consisting of the elements of this with e prepended.
*/
//@Override
public PStack<E> plus(E e);
/**
* Returns a stack consisting of the elements of this with list prepended in reverse.
*/
//@Override
public PStack<E> plusAll(Collection<? extends E> list);
//@Override
public PStack<E> with(int i, E e);
//@Override
public PStack<E> plus(int i, E e);
//@Override
public PStack<E> plusAll(int i, Collection<? extends E> list);
//@Override
public PStack<E> minus(Object e);
//@Override
public PStack<E> minusAll(Collection<?> list);
//@Override
public PStack<E> minus(int i);
//@Override
public PStack<E> subList(int start, int end);
/**
* @param start
* @return subList(start,this.size())
*/
public PStack<E> subList(int start);
}
@@ -0,0 +1,148 @@
package kotlin.reflect.jvm.internal.pcollections;
import java.util.Map;
import java.util.Map.Entry;
/**
* (Taken from Java 6 code, so pcollections can be Java 5-compatible.)
* <p>
* An Entry maintaining an immutable key and value. This class
* does not support method <tt>setValue</tt>. This class may be
* convenient in methods that return thread-safe snapshots of
* key-value mappings.
*
* @since 1.6
*/
/*public*/ final class SimpleImmutableEntry<K,V>
implements Map.Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L;
private final K key;
private final V value;
/**
* Creates an entry representing a mapping from the specified
* key to the specified value.
*
* @param key the key represented by this entry
* @param value the value represented by this entry
*/
public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
}
/**
* Creates an entry representing the same mapping as the
* specified entry.
*
* @param entry the entry to copy
*/
public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
/**
* Returns the key corresponding to this entry.
*
* @return the key corresponding to this entry
*/
public K getKey() {
return key;
}
/**
* Returns the value corresponding to this entry.
*
* @return the value corresponding to this entry
*/
public V getValue() {
return value;
}
/**
* Replaces the value corresponding to this entry with the specified
* value (optional operation). This implementation simply throws
* <tt>UnsupportedOperationException</tt>, as this class implements
* an <i>immutable</i> map entry.
*
* @param value new value to be stored in this entry
* @return (Does not return)
* @throws UnsupportedOperationException always
*/
public V setValue(V value) {
throw new UnsupportedOperationException();
}
/**
* Compares the specified object with this entry for equality.
* Returns {@code true} if the given object is also a map entry and
* the two entries represent the same mapping. More formally, two
* entries {@code e1} and {@code e2} represent the same mapping
* if<pre>
* (e1.getKey()==null ?
* e2.getKey()==null :
* e1.getKey().equals(e2.getKey()))
* &amp;&amp;
* (e1.getValue()==null ?
* e2.getValue()==null :
* e1.getValue().equals(e2.getValue()))</pre>
* This ensures that the {@code equals} method works properly across
* different implementations of the {@code Map.Entry} interface.
*
* @param o object to be compared for equality with this map entry
* @return {@code true} if the specified object is equal to this map
* entry
* @see #hashCode
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
/**
* Returns the hash code value for this map entry. The hash code
* of a map entry {@code e} is defined to be: <pre>
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
* This ensures that {@code e1.equals(e2)} implies that
* {@code e1.hashCode()==e2.hashCode()} for any two Entries
* {@code e1} and {@code e2}, as required by the general
* contract of {@link Object#hashCode}.
*
* @return the hash code value for this map entry
* @see #equals
*/
@Override
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
/**
* Returns a String representation of this map entry. This
* implementation returns the string representation of this
* entry's key followed by the equals character ("<tt>=</tt>")
* followed by the string representation of this entry's value.
*
* @return a String representation of this map entry
*/
@Override
public String toString() {
return key + "=" + value;
}
/**
* Utility method for SimpleEntry and SimpleImmutableEntry.
* Test for equality, checking for nulls.
*/
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
}
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2008 Harold Cooper
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.