blob: 4b829af0f37875855cff750b36a6de1d972f5ebd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.tests.java.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
public class RefSortedMap<K, V> extends java.util.AbstractMap<K, V>
implements SortedMap<K, V>, Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private static final class MapEntry<K, V> implements Map.Entry<K, V> {
final K key;
V value;
MapEntry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V v) {
V res = value;
value = v;
return res;
}
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode())
^ (getValue() == null ? 0 : getValue().hashCode());
}
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Map.Entry) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
return (getKey() == null ? entry.getKey() == null : getKey().equals(entry
.getKey()))
&& (getValue() == null ? entry.getValue() == null : getValue()
.equals(entry.getValue()));
}
return false;
}
}
transient ArrayList<MapEntry<K, V>> entries = new ArrayList<MapEntry<K, V>>();
transient int modCnt;
private final Comparator<? super K> comparator;
class SubMap extends java.util.AbstractMap<K, V>
implements SortedMap<K, V>, Cloneable {
final boolean hasStart, hasEnd;
final K start, end;
SubMap(boolean hasFirst, K first, boolean hasLast, K last) {
this.hasStart = hasFirst;
this.start = first;
this.hasEnd = hasLast;
this.end = last;
if (hasStart && hasEnd && compare(start, end) >= 0) {
throw new IllegalArgumentException();
}
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new AbstractSet<Entry<K, V>>() {
@Override
public Iterator<java.util.Map.Entry<K, V>> iterator() {
return new Iterator<Entry<K, V>>() {
int modCnt = RefSortedMap.this.modCnt;
int offset = SubMap.this.size() > 0 ?
bsearch(SubMap.this.firstKey()) - 1 :
entries.size();
public boolean hasNext() {
if (modCnt != RefSortedMap.this.modCnt) {
throw new ConcurrentModificationException();
}
return offset + 1 < entries.size()
&& isInRange(entries.get(offset + 1).getKey());
}
public Map.Entry<K, V> next() {
if (modCnt != RefSortedMap.this.modCnt) {
throw new ConcurrentModificationException();
}
if (!hasNext()) {
throw new NoSuchElementException();
}
offset++;
return entries.get(offset);
}
public void remove() {
if (modCnt != RefSortedMap.this.modCnt) {
throw new ConcurrentModificationException();
}
modCnt++;
RefSortedMap.this.modCnt++;
RefSortedMap.this.entries.remove(offset);
offset--;
}
};
}
@Override
public int size() {
try {
int lastIdx = bsearch(SubMap.this.lastKey());
int firstIdx = bsearch(SubMap.this.firstKey());
return lastIdx - firstIdx + 1;
} catch (NoSuchElementException e) {
return 0;
} catch (ArrayIndexOutOfBoundsException e) {
return 0;
}
}
};
}
public Comparator<? super K> comparator() {
return RefSortedMap.this.comparator();
}
public K firstKey() {
if (!hasStart) {
K res = RefSortedMap.this.firstKey();
if (!isInRange(res)) {
throw new NoSuchElementException();
}
return res;
}
int idx = bsearch(start);
if (idx >= 0) {
return start;
}
if (-idx - 1 >= entries.size() || !isInRange(entries.get(-idx - 1).getKey())) {
throw new NoSuchElementException();
}
return entries.get(-idx - 1).getKey();
}
public SortedMap<K, V> headMap(K key) {
if (!isInRange(key)) {
throw new IllegalArgumentException();
}
return new SubMap(hasStart, start, true, key);
}
public K lastKey() {
if (!hasEnd) {
K res = RefSortedMap.this.lastKey();
if (!isInRange(res)) {
throw new NoSuchElementException();
}
return res;
}
int idx = bsearch(end);
idx = idx >= 0 ? idx - 1 : -idx - 2;
if (idx < 0 || !isInRange(entries.get(idx).getKey())) {
throw new NoSuchElementException();
}
return entries.get(idx).getKey();
}
public SortedMap<K, V> subMap(K startKey, K endKey) {
if (!isInRange(startKey)) {
throw new IllegalArgumentException();
}
if (!isInRange(endKey)) {
throw new IllegalArgumentException();
}
return new SubMap(true, startKey, true, endKey);
}
public SortedMap<K, V> tailMap(K key) {
if (!isInRange(key)) {
throw new IllegalArgumentException();
}
return new SubMap(true, key, hasEnd, end);
}
private boolean isInRange(K key) {
if (hasStart && compare(key, start) < 0) {
return false;
}
if (hasEnd && compare(key, end) >= 0) {
return false;
}
return true;
}
}
public RefSortedMap() {
this((Comparator<? super K>) null);
}
@SuppressWarnings("unchecked")
public int compare(K start, K end) {
return comparator != null ? comparator.compare(start, end)
: ((Comparable<K>) start).compareTo(end);
}
@SuppressWarnings("unchecked")
public RefSortedMap(Comparator<? super K> comparator) {
this.comparator = comparator;
cmp = createCmp();
}
public RefSortedMap(Map<? extends K, ? extends V> map) {
this();
putAll(map);
}
public RefSortedMap(SortedMap<K, ? extends V> map) {
this(map.comparator());
putAll(map);
}
public Comparator<? super K> comparator() {
return comparator;
}
public Set<Map.Entry<K, V>> entrySet() {
return tailMap(firstKey()).entrySet();
}
public K firstKey() {
return entries.get(0).getKey();
}
public SortedMap<K, V> headMap(K key) {
return new SubMap(false, null, true, key);
}
public Set<K> keySet() {
return tailMap(firstKey()).keySet();
}
public K lastKey() {
return entries.get(entries.size() - 1).getKey();
}
public SortedMap<K, V> subMap(K startKey, K endKey) {
return new SubMap(true, startKey, true, endKey);
}
public SortedMap<K, V> tailMap(K key) {
return new SubMap(true, key, false, null);
}
public Collection<V> values() {
return tailMap(firstKey()).values();
}
public void clear() {
entries.clear();
}
public boolean containsKey(Object arg0) {
return bsearch(arg0) >= 0;
}
public boolean containsValue(Object arg0) {
for (V v : values()) {
if (arg0.equals(v)) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
public V get(Object arg0) {
int idx = bsearch(arg0);
return idx >= 0 ? entries.get(idx).getValue() : null;
}
public boolean isEmpty() {
return entries.isEmpty();
}
public V put(K arg0, V arg1) {
modCnt++;
int idx = bsearch(arg0);
if (idx >= 0) {
return entries.get(idx).setValue(arg1);
}
entries.add(-idx - 1, new MapEntry<K, V>(arg0, arg1));
return null;
}
public void putAll(Map<? extends K, ? extends V> arg0) {
for (Map.Entry<? extends K, ? extends V> e : arg0.entrySet()) {
put(e.getKey(), e.getValue());
}
}
@SuppressWarnings("unchecked")
public V remove(Object arg0) {
modCnt++;
int idx = bsearch(arg0);
if (idx < 0) {
return null;
}
return entries.remove(idx).getValue();
}
transient private Comparator<MapEntry<K, V>> cmp = createCmp();
Comparator<MapEntry<K, V>> createCmp() {
return new Comparator<MapEntry<K, V>>() {
public int compare(MapEntry<K, V> arg0, MapEntry<K, V> arg1) {
return RefSortedMap.this.compare(arg0.getKey(), arg1.getKey());
}
};
}
@SuppressWarnings("unchecked")
private int bsearch(Object arg0) {
return Collections.binarySearch(entries, new MapEntry<K, V>((K) arg0, null), cmp);
}
public int size() {
return entries.size();
}
public RefSortedMap<K, V> clone() {
return new RefSortedMap<K, V>(this);
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeInt(size());
for (Map.Entry<K, V> e : entrySet()) {
stream.writeObject(e.getKey());
stream.writeObject(e.getValue());
}
}
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream stream) throws IOException,
ClassNotFoundException {
cmp = createCmp();
stream.defaultReadObject();
int size = stream.readInt();
entries = new ArrayList<MapEntry<K, V>>(size);
for (int i = 0; i < size; i++) {
put((K) stream.readObject(), (V) stream.readObject());
}
}
}