Return-Path: Delivered-To: apmail-harmony-commits-archive@www.apache.org Received: (qmail 24555 invoked from network); 13 Feb 2008 15:34:19 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 13 Feb 2008 15:34:19 -0000 Received: (qmail 62255 invoked by uid 500); 13 Feb 2008 15:34:13 -0000 Delivered-To: apmail-harmony-commits-archive@harmony.apache.org Received: (qmail 62247 invoked by uid 500); 13 Feb 2008 15:34:13 -0000 Mailing-List: contact commits-help@harmony.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@harmony.apache.org Delivered-To: mailing list commits@harmony.apache.org Received: (qmail 62238 invoked by uid 99); 13 Feb 2008 15:34:13 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Feb 2008 07:34:13 -0800 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Feb 2008 15:33:49 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 496131A983E; Wed, 13 Feb 2008 07:33:57 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r627476 - in /harmony/enhanced/classlib/trunk/modules/luni/src: main/java/java/util/ test/api/common/org/apache/harmony/luni/tests/java/util/ Date: Wed, 13 Feb 2008 15:33:54 -0000 To: commits@harmony.apache.org From: tellison@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080213153357.496131A983E@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: tellison Date: Wed Feb 13 07:33:52 2008 New Revision: 627476 URL: http://svn.apache.org/viewvc?rev=627476&view=rev Log: Apply patch for HARMONY-5475 ([classlib][luni]TreeMap problems) including slightly modified version of test case (copyright notices, and package and type renaming). Added: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/RefSortedMap.java (with props) harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/SortedMapTestBase.java (with props) harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/TreeMapRndTest.java (with props) Modified: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/TreeMap.java Modified: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/TreeMap.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/TreeMap.java?rev=627476&r1=627475&r2=627476&view=diff ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/TreeMap.java (original) +++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/TreeMap.java Wed Feb 13 07:33:52 2008 @@ -43,6 +43,82 @@ transient Set> entrySet; transient Node root; + +class MapEntry implements Map.Entry, Cloneable { + + final int offset; + final Node node; + final K key; + + MapEntry(Node node, int offset) { + this.node = node; + this.offset = offset; + key = node.keys[offset]; + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) object; + V value = getValue(); + return (key == null ? entry.getKey() == null : key.equals(entry + .getKey())) + && (value == null ? entry.getValue() == null : value + .equals(entry.getValue())); + } + return false; + } + + public K getKey() { + return key; + } + + public V getValue() { + if (node.keys[offset] == key) { + return node.values[offset]; + } + if (containsKey(key)) { + return get(key); + } + throw new IllegalStateException(); + } + + @Override + public int hashCode() { + V value = getValue(); + return (key == null ? 0 : key.hashCode()) + ^ (value == null ? 0 : value.hashCode()); + } + + public V setValue(V object) { + if (node.keys[offset] == key) { + V res = node.values[offset]; + node.values[offset] = object; + return res; + } + if (containsKey(key)) { + return put(key, object); + } + throw new IllegalStateException(); + } + + @Override + public String toString() { + return key + "=" + getValue(); + } + } static class Node implements Cloneable { static final int NODE_SIZE = 64; @@ -137,7 +213,7 @@ if (expectedModCount == backingMap.modCount) { if (lastNode != null) { int idx = lastNode.right_idx - lastOffset; - backingMap.remove(lastNode, idx); + backingMap.removeFromIterator(lastNode, idx); lastNode = null; expectedModCount++; } else { @@ -163,7 +239,7 @@ public Map.Entry next() { makeNext(); int idx = lastNode.right_idx - lastOffset; - return new MapEntry(lastNode.keys[idx], lastNode.values[idx]); + return backingMap.new MapEntry(lastNode, idx); } } @@ -208,7 +284,7 @@ BoundedMapIterator(Node startNode, int startOffset, TreeMap map, Node finalNode, int finalOffset) { - super(map, startNode, startOffset); + super(map, finalNode==null? null : startNode, startOffset); this.finalNode = finalNode; this.finalOffset = finalOffset; } @@ -227,9 +303,8 @@ } void makeBoundedNext() { - boolean endOfIterator = node == finalNode && offset == finalOffset; makeNext(); - if (endOfIterator) { + if (lastNode == finalNode && lastOffset == finalOffset) { node = null; } } @@ -246,7 +321,7 @@ public Map.Entry next() { makeBoundedNext(); int idx = lastNode.right_idx - lastOffset; - return new MapEntry(lastNode.keys[idx], lastNode.values[idx]); + return backingMap.new MapEntry(lastNode, idx); } } @@ -1739,7 +1814,7 @@ node = node.left; } else if (result == 0) { V value = node.values[left_idx]; - remove(node, left_idx); + removeLeftmost(node); return value; } else { int right_idx = node.right_idx; @@ -1750,7 +1825,7 @@ node = node.right; } else if (result == 0) { V value = node.values[right_idx]; - remove(node, right_idx); + removeRightmost(node); return value; } else { /*search in node*/ int low = left_idx + 1, mid = 0, high = right_idx - 1; @@ -1761,7 +1836,7 @@ low = mid + 1; } else if (result == 0) { V value = node.values[mid]; - remove(node, mid); + removeMiddleElement(node, mid); return value; } else { high = mid - 1; @@ -1774,133 +1849,290 @@ return null; } - void remove(Node node, int index) { + void removeLeftmost(Node node) { + int index = node.left_idx; if (node.size == 1) { deleteNode(node); } else if (node.prev != null && (Node.NODE_SIZE - 1 - node.prev.right_idx) > node.size) { // move all to prev node and kill it Node prev = node.prev; - int left_idx = node.left_idx; - if (index != left_idx) { - int size = index - left_idx; - System.arraycopy(node.keys, left_idx, prev.keys, prev.right_idx + 1, size); - System.arraycopy(node.values, left_idx, prev.values, prev.right_idx + 1, size); - prev.right_idx += size; - } - int right_idx = node.right_idx; - if (index != right_idx) { - int size = right_idx - index; - System.arraycopy(node.keys, index + 1, prev.keys, prev.right_idx + 1, size); - System.arraycopy(node.values, index + 1, prev.values, prev.right_idx + 1, size); - prev.right_idx += size; + int size = node.right_idx - index; + System.arraycopy(node.keys, index + 1, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, index + 1, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + prev.size += size; + deleteNode(node); + } else if (node.next != null && (node.next.left_idx) > node.size) { + // move all to next node and kill it + Node next = node.next; + int size = node.right_idx - index; + int next_new_left = next.left_idx - size; + next.left_idx = next_new_left; + System.arraycopy(node.keys, index + 1, next.keys, next_new_left, size); + System.arraycopy(node.values, index + 1, next.values, next_new_left, size); + next.size += size; + deleteNode(node); + } else { + node.keys[index] = null; + node.values[index] = null; + node.left_idx++; + node.size--; + Node prev = node.prev; + if (prev != null && prev.size == 1) { + node.size++; + node.left_idx--; + node.keys [node.left_idx] = prev.keys [prev.left_idx]; + node.values[node.left_idx] = prev.values[prev.left_idx]; + deleteNode(prev); } - prev.size += (node.size - 1); + } + modCount++; + size--; + } + + void removeRightmost(Node node) { + int index = node.right_idx; + if (node.size == 1) { + deleteNode(node); + } else if (node.prev != null && (Node.NODE_SIZE - 1 - node.prev.right_idx) > node.size) { + // move all to prev node and kill it + Node prev = node.prev; + int left_idx = node.left_idx; + int size = index - left_idx; + System.arraycopy(node.keys, left_idx, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, left_idx, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + prev.size += size; deleteNode(node); } else if (node.next != null && (node.next.left_idx) > node.size) { // move all to next node and kill it Node next = node.next; int left_idx = node.left_idx; - int next_new_left = next.left_idx + node.size - 1; + int size = index - left_idx; + int next_new_left = next.left_idx - size; next.left_idx = next_new_left; - if (index != left_idx) { - int size = index - left_idx; - System.arraycopy(node.keys, left_idx, next.keys, next_new_left, size); - System.arraycopy(node.values, left_idx, next.values, next_new_left, size); - next_new_left += size; - } - int right_idx = node.right_idx; - if (index != right_idx) { - int size = right_idx - index; - System.arraycopy(node.keys, index + 1, next.keys, next_new_left, size); - System.arraycopy(node.values, index + 1, next.values, next_new_left, size); - } - next.size += (node.size - 1); + System.arraycopy(node.keys, left_idx, next.keys, next_new_left, size); + System.arraycopy(node.values, left_idx, next.values, next_new_left, size); + next.size += size; deleteNode(node); } else { - if (index == node.left_idx) { - node.keys[index] = null; - node.values[index] = null; - node.left_idx++; - } else if (index == node.right_idx) { - node.keys[index] = null; - node.values[index] = null; - node.right_idx--; - } else if (node.right_idx - index <= index - node.left_idx) { - System.arraycopy(node.keys, index + 1, node.keys, index, node.right_idx - index); - System.arraycopy(node.values, index + 1, node.values, index, node.right_idx - index); - node.right_idx--; - } else { - System.arraycopy(node.keys, node.left_idx, node.keys, node.left_idx + 1, index - node.left_idx); - System.arraycopy(node.values, node.left_idx, node.values, node.left_idx + 1, index - node.left_idx); - node.left_idx++; - } + node.keys[index] = null; + node.values[index] = null; + node.right_idx--; node.size--; Node next = node.next; - Node prev = node.prev; - if (next != null && node.right_idx < Node.NODE_SIZE - 1 && next.size == 1) { + if (next != null && next.size == 1) { node.size++; node.right_idx++; node.keys[node.right_idx] = next.keys[next.left_idx]; node.values[node.right_idx] = next.values[next.left_idx]; deleteNode(next); - } else if (prev != null && node.left_idx > 0 && prev.size == 1) { - node.size++; - node.left_idx--; - node.keys[node.left_idx] = prev.keys[prev.left_idx]; - node.values[node.left_idx] = prev.values[prev.left_idx]; - deleteNode(prev); } } modCount++; size--; } - private void deleteNode(Node node) { - Node toDelete, toConnect; - if (node.right == null || node.left == null) { - toDelete = node; + void removeMiddleElement(Node node, int index) { + // this function is called iff index if some middle element; + // so node.left_idx < index < node.right_idx + // condition above assume that node.size > 1 + if (node.prev != null && (Node.NODE_SIZE - 1 - node.prev.right_idx) > node.size) { + // move all to prev node and kill it + Node prev = node.prev; + int left_idx = node.left_idx; + int size = index - left_idx; + System.arraycopy(node.keys, left_idx, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, left_idx, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + size = node.right_idx - index; + System.arraycopy(node.keys, index + 1, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, index + 1, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + prev.size += (node.size - 1); + deleteNode(node); + } else if (node.next != null && (node.next.left_idx) > node.size) { + // move all to next node and kill it + Node next = node.next; + int left_idx = node.left_idx; + int next_new_left = next.left_idx - node.size + 1; + next.left_idx = next_new_left; + int size = index - left_idx; + System.arraycopy(node.keys, left_idx, next.keys, next_new_left, size); + System.arraycopy(node.values, left_idx, next.values, next_new_left, size); + next_new_left += size; + size = node.right_idx - index; + System.arraycopy(node.keys, index + 1, next.keys, next_new_left, size); + System.arraycopy(node.values, index + 1, next.values, next_new_left, size); + next.size += (node.size - 1); + deleteNode(node); } else { - toDelete = node.next; + int moveFromRight = node.right_idx - index; + int left_idx = node.left_idx; + int moveFromLeft = index - left_idx ; + if (moveFromRight <= moveFromLeft) { + System.arraycopy(node.keys, index + 1, node.keys, index, moveFromRight); + System.arraycopy(node.values, index + 1, node.values, index, moveFromRight); + Node next = node.next; + if (next != null && next.size == 1) { + node.keys [node.right_idx] = next.keys [next.left_idx]; + node.values[node.right_idx] = next.values[next.left_idx]; + deleteNode(next); + } else { + node.keys [node.right_idx] = null; + node.values[node.right_idx] = null; + node.right_idx--; + node.size--; + } + } else { + System.arraycopy(node.keys, left_idx , node.keys, left_idx + 1, moveFromLeft); + System.arraycopy(node.values, left_idx , node.values, left_idx + 1, moveFromLeft); + Node prev = node.prev; + if (prev != null && prev.size == 1) { + node.keys [left_idx ] = prev.keys [prev.left_idx]; + node.values[left_idx ] = prev.values[prev.left_idx]; + deleteNode(prev); + } else { + node.keys [left_idx ] = null; + node.values[left_idx ] = null; + node.left_idx++; + node.size--; + } + } } - if (toDelete.left != null) { - toConnect = toDelete.left; + modCount++; + size--; + } + + void removeFromIterator(Node node, int index) { + if (node.size == 1) { + // it is safe to delete the whole node here. + // iterator already moved to the next node; + deleteNode(node); } else { - toConnect = toDelete.right; + int left_idx = node.left_idx; + if (index == left_idx) { + Node prev = node.prev; + if (prev != null && prev.size == 1) { + node.keys [left_idx] = prev.keys [prev.left_idx]; + node.values[left_idx] = prev.values[prev.left_idx]; + deleteNode(prev); + } else { + node.keys [left_idx] = null; + node.values[left_idx] = null; + node.left_idx++; + node.size--; + } + } else if (index == node.right_idx) { + node.keys [index] = null; + node.values[index] = null; + node.right_idx--; + node.size--; + } else { + int moveFromRight = node.right_idx - index; + int moveFromLeft = index - left_idx; + if (moveFromRight <= moveFromLeft) { + System.arraycopy(node.keys, index + 1, node.keys, index, moveFromRight ); + System.arraycopy(node.values, index + 1, node.values, index, moveFromRight ); + node.keys [node.right_idx] = null; + node.values[node.right_idx] = null; + node.right_idx--; + node.size--; + } else { + System.arraycopy(node.keys, left_idx, node.keys, left_idx+ 1, moveFromLeft); + System.arraycopy(node.values, left_idx, node.values, left_idx+ 1, moveFromLeft); + node.keys [left_idx] = null; + node.values[left_idx] = null; + node.left_idx++; + node.size--; + } + } } - if (toConnect != null) { - toConnect.parent = toDelete.parent; + modCount++; + size--; + } + + private void deleteNode(Node node) { + if (node.right == null) { + if (node.left != null) { + attachToParent(node, node.left); + } else { + attachNullToParent(node); + } + fixNextChain(node); + } else if(node.left == null) { // node.right != null + attachToParent(node, node.right); + fixNextChain(node); + } else { + // Here node.left!=nul && node.right!=null + // node.next should replace node in tree + // node.next!=null by tree logic. + // node.next.left==null by tree logic. + // node.next.right may be null or non-null + Node toMoveUp = node.next; + fixNextChain(node); + if(toMoveUp.right==null){ + attachNullToParent(toMoveUp); + } else { + attachToParent(toMoveUp, toMoveUp.right); + } + // Here toMoveUp is ready to replace node + toMoveUp.left = node.left; + if (node.left != null) { + node.left.parent = toMoveUp; + } + toMoveUp.right = node.right; + if (node.right != null) { + node.right.parent = toMoveUp; + } + attachToParentNoFixup(node,toMoveUp); + toMoveUp.color = node.color; } - if (toDelete.parent == null) { + } + + private void attachToParentNoFixup(Node toDelete, Node toConnect) { + // assert toConnect!=null + Node parent = toDelete.parent; + toConnect.parent = parent; + if (parent == null) { root = toConnect; - } else if (toDelete == toDelete.parent.left) { - toDelete.parent.left = toConnect; + } else if (toDelete == parent.left) { + parent.left = toConnect; } else { - toDelete.parent.right = toConnect; + parent.right = toConnect; } - if (toDelete != node) { - node.keys = toDelete.keys; - node.values = toDelete.values; - node.size = toDelete.size; - node.left_idx = toDelete.left_idx; - node.right_idx = toDelete.right_idx; - node.next = toDelete.next; - if (node.next != null) { - node.next.prev = node; - } + } + + private void attachToParent(Node toDelete, Node toConnect) { + // assert toConnect!=null + attachToParentNoFixup(toDelete,toConnect); + if (!toDelete.color) { + fixup(toConnect); + } + } + + private void attachNullToParent(Node toDelete) { + Node parent = toDelete.parent; + if (parent == null) { + root = null; } else { - if (node.prev != null) { - node.prev.next = node.next; + if (toDelete == parent.left) { + parent.left = null; + } else { + parent.right = null; } - if (node.next != null) { - node.next.prev = node.prev; + if (!toDelete.color) { + fixup(parent); } } - if (!toDelete.color && root != null) { - if (toConnect == null) { - fixup(toDelete.parent); - } else { - fixup(toConnect); - } + } + + private void fixNextChain(Node node) { + if (node.prev != null) { + node.prev.next = node.next; + } + if (node.next != null) { + node.next.prev = node.prev; } } Added: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/RefSortedMap.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/RefSortedMap.java?rev=627476&view=auto ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/RefSortedMap.java (added) +++ harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/RefSortedMap.java Wed Feb 13 07:33:52 2008 @@ -0,0 +1,402 @@ +/* + * 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.luni.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 extends java.util.AbstractMap + implements SortedMap, Cloneable, Serializable { + + private static final long serialVersionUID = 1L; + + private static final class MapEntry implements Map.Entry { + + 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> entries = new ArrayList>(); + transient int modCnt; + + private final Comparator comparator; + + class SubMap extends java.util.AbstractMap + implements SortedMap, 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> entrySet() { + return new AbstractSet> () { + + @Override + public Iterator> iterator() { + return new Iterator>() { + 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 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 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 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 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 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) null); + } + + @SuppressWarnings("unchecked") + public int compare(K start, K end) { + return comparator != null ? comparator.compare(start, end) + : ((Comparable) start).compareTo(end); + } + + @SuppressWarnings("unchecked") + public RefSortedMap(Comparator comparator) { + this.comparator = comparator; + cmp = createCmp(); + } + + public RefSortedMap(Map map) { + this(); + putAll(map); + } + + public RefSortedMap(SortedMap map) { + this(map.comparator()); + putAll(map); + } + + public Comparator comparator() { + return comparator; + } + + public Set> entrySet() { + return tailMap(firstKey()).entrySet(); + } + + public K firstKey() { + return entries.get(0).getKey(); + } + + public SortedMap headMap(K key) { + return new SubMap(false, null, true, key); + } + + public Set keySet() { + return tailMap(firstKey()).keySet(); + } + + public K lastKey() { + return entries.get(entries.size() - 1).getKey(); + } + + public SortedMap subMap(K startKey, K endKey) { + return new SubMap(true, startKey, true, endKey); + } + + public SortedMap tailMap(K key) { + return new SubMap(true, key, false, null); + } + + public Collection 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(arg0, arg1)); + return null; + } + + public void putAll(Map arg0) { + for (Map.Entry 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> cmp = createCmp(); + + Comparator> createCmp() { + return new Comparator>() { + + public int compare(MapEntry arg0, MapEntry arg1) { + return RefSortedMap.this.compare(arg0.getKey(), arg1.getKey()); + } + + }; + } + + @SuppressWarnings("unchecked") + private int bsearch(Object arg0) { + return Collections.binarySearch(entries, new MapEntry((K) arg0, null), cmp); + } + + public int size() { + return entries.size(); + } + + public RefSortedMap clone() { + return new RefSortedMap(this); + } + + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(size()); + for (Map.Entry 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>(size); + for (int i = 0; i < size; i++) { + put((K) stream.readObject(), (V) stream.readObject()); + } + } + +} Propchange: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/RefSortedMap.java ------------------------------------------------------------------------------ svn:eol-style = native Added: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/SortedMapTestBase.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/SortedMapTestBase.java?rev=627476&view=auto ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/SortedMapTestBase.java (added) +++ harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/SortedMapTestBase.java Wed Feb 13 07:33:52 2008 @@ -0,0 +1,369 @@ +/* + * 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.luni.tests.java.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import junit.framework.TestCase; + +public abstract class SortedMapTestBase extends TestCase { + + final int N = 1000; + final int TRIES = 100; + + SortedMap map; + SortedMap ref; + + Random rnd; + + protected void setUp() throws Exception { + rnd = new Random(-1); + for (int i = 0; i < N ; i++) { + ref.put(rnd.nextInt(N) * 2, rnd.nextBoolean() ? null : rnd.nextInt(N) * 2); + } + } + + public final void testClear() { + map.clear(); + assertTrue(map.isEmpty()); + } + + public final void testContainsKey() { + for (int i = 0; i < TRIES; i++) { + int key = rnd.nextInt(N); + assertEquals(ref.containsKey(key), map.containsKey(key)); + } + } + + + public final void testContainsValue() { + for (int i = 0; i < TRIES; i++) { + int value = rnd.nextInt(N); + assertEquals(ref.containsValue(value), map.containsValue(value)); + } + } + + + public final void testEntrySet() { + Set> refSet = ref.entrySet(); + Set> mapSet = map.entrySet(); + for (Map.Entry e : refSet) { + assertTrue(mapSet.contains(e)); + } + for (Map.Entry e : mapSet) { + assertTrue(refSet.contains(e)); + } + assertEquals(ref.entrySet(), map.entrySet()); + } + + + public final void testGet() { + for (int i = 0; i < TRIES; i++) { + int key = rnd.nextInt(N); + assertEquals(ref.get(key), map.get(key)); + } + } + + + public final void testKeySet() { + assertEquals(ref.keySet(), map.keySet()); + Iterator i = ref.keySet().iterator(); + Iterator j = map.keySet().iterator(); + while (i.hasNext()) { + assertEquals(i.next(), j.next()); + if (rnd.nextBoolean()) { + j.remove(); + i.remove(); + } + } + } + + + public final void testPut() { + for (int i = 0; i < TRIES; i++) { + int key = rnd.nextInt(N); + int value = rnd.nextInt(N); + assertEquals(ref.put(key, value), map.put(key, value)); + assertEquals(ref.get(key), map.get(key)); + assertEquals(ref, map); + } + } + + public final void testPut0() { + ref.clear(); + map.clear(); + for (int i = 0; i < N; i++) { + int key = rnd.nextInt(N); + int value = rnd.nextInt(N); + assertEquals(ref.put(key, value), map.put(key, value)); + assertEquals(ref.get(key), map.get(key)); + } + } + + public final void testPutAll() { + Map mixin = new HashMap(TRIES); + for (int i = 0; i < TRIES; i++) { + mixin.put(rnd.nextInt(N), rnd.nextInt(N)); + } + ref.putAll(mixin); + map.putAll(mixin); + assertEquals(ref, map); + } + + + public final void testRemove() { + for (int i = 0; i < N; i++) { + int key = rnd.nextInt(N); + assertEquals(ref.remove(key), map.remove(key)); + if (i % (N / TRIES) == 0) { + assertEquals(ref, map); + } + } + } + + public final void testRemove0() { + while (!ref.isEmpty()) { + int key = ref.tailMap((ref.firstKey() + ref.lastKey()) / 2) + .firstKey(); + assertEquals(ref.remove(key), map.remove(key)); + } + } + + public final void testSize() { + assertEquals(ref.size(), map.size()); + } + + + public final void testValues() { + assertEquals(ref.values().size(), map.values().size()); + assertTrue(ref.values().containsAll(map.values())); + assertTrue(map.values().containsAll(ref.values())); + + Iterator i = ref.values().iterator(); + Iterator j = map.values().iterator(); + while (i.hasNext()) { + assertEquals(i.next(), j.next()); + if (rnd.nextBoolean()) { + j.remove(); + i.remove(); + } + } + } + + public final void testComparator() { + assertEquals(ref.comparator(), map.comparator()); + } + + + public final void testFirstKey() { + assertEquals(ref.firstKey(), map.firstKey()); + } + + + public final void testHeadMap() { + for (int i = 0; i < TRIES; i++) { + int key = rnd.nextInt(N); + checkSubMap(ref.headMap(key), map.headMap(key)); + } + checkSubMap(ref.headMap(-1), map.headMap(-1)); + } + + public final void testLastKey() { + assertEquals(ref.lastKey(), map.lastKey()); + } + + public final void testSubMap() { + for (int i = 0; i < TRIES; i++) { + int key0 = rnd.nextInt(N/2); + int key1 = rnd.nextInt(N/2) + N/2; + if (ref.comparator() != null && + ref.comparator().compare(key0, key1) > 0) { + + int tmp = key0; + key0 = key1; + key1 = tmp; + } + checkSubMap(ref.subMap(key0, key1), map.subMap(key0, key1)); + } + boolean caught = false; + try { + if (ref.comparator() != null && ref.comparator().compare(100, 0) < 0) { + map.subMap(0, 100); + } else { + map.subMap(100, 0); + } + } catch (IllegalArgumentException e) { + caught = true; + } + assertTrue(caught); + + int firstKey = ref.firstKey(); + Map.Entry refE = ref.entrySet().iterator().next(); + Map.Entry mapE = map.entrySet().iterator().next(); + mapE.setValue(-1); + refE.setValue(-1); + assertEquals(ref.get(firstKey), map.get(firstKey)); + } + + + public final void testTailMap() { + for (int i = 0; i < TRIES; i++) { + int key = rnd.nextInt(2 * N); + checkSubMap(ref.tailMap(key), map.tailMap(key)); + } + checkSubMap(ref.tailMap(2 * N + 1), map.tailMap(2 * N + 1)); + } + + + public final void testHashCode() { + assertEquals(ref.hashCode(), map.hashCode()); + } + + public final void testEqualsObject() { + assertTrue(map.equals(ref)); + map.put(N + 1, N + 1); + assertFalse(map.equals(ref)); + } + + + public final void testIsEmpty() { + assertEquals(ref.isEmpty(), map.isEmpty()); + } + + public final void testIsEmpty2() { + TreeMap map = new TreeMap(); + map.put("one", "1"); + assertEquals("size should be one", 1, map.size()); + map.clear(); + assertEquals("size should be zero", 0, map.size()); + assertTrue("Should not have entries", !map.entrySet().iterator() + .hasNext()); + + map.put("one", "1"); + assertEquals("size should be one", 1, map.size()); + map.remove("one"); + assertEquals("size should be zero", 0, map.size()); + assertTrue("Should not have entries", !map.entrySet().iterator() + .hasNext()); + + map.clear(); + map.put("0", "1"); + map.clear(); + assertTrue(map.isEmpty()); + assertFalse(map.entrySet().iterator().hasNext()); + assertFalse(map.keySet().iterator().hasNext()); + assertFalse(map.values().iterator().hasNext()); + } + + public final void testToString() { + assertEquals(ref.toString(), map.toString()); + } + + private void checkSubMap(SortedMap ref, + SortedMap map) { + + assertEquals(ref.size(), map.size()); + assertEquals(ref, map); + assertEquals(ref.isEmpty(), map.isEmpty()); + if (!ref.isEmpty()) { + assertEquals(ref.firstKey(), map.firstKey()); + assertEquals(ref.lastKey(), map.lastKey()); + + testViews(ref, map); + } else { + boolean caught = false; + try { + map.firstKey(); + } catch (NoSuchElementException e) { + caught = true; + } + caught = false; + try { + map.lastKey(); + } catch (NoSuchElementException e) { + caught = true; + } + assertTrue(caught); + } + + } + + public final void testViews() { + testViews(ref, map); + } + + private void testViews(SortedMap ref, SortedMap map) { + assertEquals(ref.keySet().size(), map.keySet().size()); + assertEquals(ref.keySet(), map.keySet()); + compareIterators(ref.keySet(), map.keySet()); + + assertEquals(ref.values().size(), map.values().size()); + compareIterators(ref.values(), map.values()); + + assertEquals(ref.entrySet(), map.entrySet()); + compareIterators(ref.entrySet(), map.entrySet()); + } + + private void compareIterators(Collection ref, Collection map) { + Iterator i = ref.iterator(); + Iterator j = map.iterator(); + while (i.hasNext()) { + assertEquals(i.next(), j.next()); + if (rnd.nextBoolean()) { + j.remove(); + i.remove(); + assertEquals(ref.size(), map.size()); + } + } + } + + @SuppressWarnings("unchecked") + public final void testSerialization() throws IOException, ClassNotFoundException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(map); + oos.close(); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); + Object read = ois.readObject(); + assertEquals(ref, read); + } + + public final void testClone() throws Exception { + Method refClone = ref.getClass().getMethod("clone", null); + Method mapClone = map.getClass().getMethod("clone", null); + SortedMap map2 = (SortedMap) mapClone.invoke(map, null); + assertEquals(refClone.invoke(ref, null), map2); + map2.remove(map2.lastKey()); + assertFalse(ref.equals(map2)); + } + +} Propchange: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/SortedMapTestBase.java ------------------------------------------------------------------------------ svn:eol-style = native Added: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/TreeMapRndTest.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/TreeMapRndTest.java?rev=627476&view=auto ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/TreeMapRndTest.java (added) +++ harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/TreeMapRndTest.java Wed Feb 13 07:33:52 2008 @@ -0,0 +1,28 @@ +/* + * 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.luni.tests.java.util; + +import java.util.TreeMap; + +public class TreeMapRndTest extends SortedMapTestBase { + protected void setUp() throws Exception { + ref = new RefSortedMap(); + super.setUp(); + map = new TreeMap(ref); + } +} Propchange: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/util/TreeMapRndTest.java ------------------------------------------------------------------------------ svn:eol-style = native