diff --git a/gson/src/main/java/com/google/gson/internal/LinkedHashTreeMap.java b/gson/src/main/java/com/google/gson/internal/LinkedHashTreeMap.java
deleted file mode 100644
index 0cade0d1..00000000
--- a/gson/src/main/java/com/google/gson/internal/LinkedHashTreeMap.java
+++ /dev/null
@@ -1,872 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- * Copyright (C) 2012 Google Inc.
- *
- * Licensed 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 com.google.gson.internal;
-
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
-import java.io.ObjectStreamException;
-import java.io.Serializable;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.ConcurrentModificationException;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-/**
- * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
- * insertion order for iteration order. Comparison order is only used as an
- * optimization for efficient insertion and removal.
- *
- *
This implementation was derived from Android 4.1's TreeMap and
- * LinkedHashMap classes.
- */
-public final class LinkedHashTreeMap extends AbstractMap implements Serializable {
- @SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable>>
- private static final Comparator NATURAL_ORDER = new Comparator() {
- public int compare(Comparable a, Comparable b) {
- return a.compareTo(b);
- }
- };
-
- Comparator super K> comparator;
- Node[] table;
- final Node header;
- int size = 0;
- int modCount = 0;
- int threshold;
-
- /**
- * Create a natural order, empty tree map whose keys must be mutually
- * comparable and non-null.
- */
- @SuppressWarnings("unchecked") // unsafe! this assumes K is comparable
- public LinkedHashTreeMap() {
- this((Comparator super K>) NATURAL_ORDER);
- }
-
- /**
- * Create a tree map ordered by {@code comparator}. This map's keys may only
- * be null if {@code comparator} permits.
- *
- * @param comparator the comparator to order elements with, or {@code null} to
- * use the natural ordering.
- */
- @SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable
- public LinkedHashTreeMap(Comparator super K> comparator) {
- this.comparator = comparator != null
- ? comparator
- : (Comparator) NATURAL_ORDER;
- this.header = new Node();
- this.table = new Node[16]; // TODO: sizing/resizing policies
- this.threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
- }
-
- @Override public int size() {
- return size;
- }
-
- @Override public V get(Object key) {
- Node node = findByObject(key);
- return node != null ? node.value : null;
- }
-
- @Override public boolean containsKey(Object key) {
- return findByObject(key) != null;
- }
-
- @Override public V put(K key, V value) {
- if (key == null) {
- throw new NullPointerException("key == null");
- }
- Node created = find(key, true);
- V result = created.value;
- created.value = value;
- return result;
- }
-
- @Override public void clear() {
- Arrays.fill(table, null);
- size = 0;
- modCount++;
-
- // Clear all links to help GC
- Node header = this.header;
- for (Node e = header.next; e != header; ) {
- Node next = e.next;
- e.next = e.prev = null;
- e = next;
- }
-
- header.next = header.prev = header;
- }
-
- @Override public V remove(Object key) {
- Node node = removeInternalByKey(key);
- return node != null ? node.value : null;
- }
-
- /**
- * Returns the node at or adjacent to the given key, creating it if requested.
- *
- * @throws ClassCastException if {@code key} and the tree's keys aren't
- * mutually comparable.
- */
- Node find(K key, boolean create) {
- Comparator super K> comparator = this.comparator;
- Node[] table = this.table;
- int hash = secondaryHash(key.hashCode());
- int index = hash & (table.length - 1);
- Node nearest = table[index];
- int comparison = 0;
-
- if (nearest != null) {
- // Micro-optimization: avoid polymorphic calls to Comparator.compare().
- @SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
- Comparable comparableKey = (comparator == NATURAL_ORDER)
- ? (Comparable) key
- : null;
-
- while (true) {
- comparison = (comparableKey != null)
- ? comparableKey.compareTo(nearest.key)
- : comparator.compare(key, nearest.key);
-
- // We found the requested key.
- if (comparison == 0) {
- return nearest;
- }
-
- // If it exists, the key is in a subtree. Go deeper.
- Node child = (comparison < 0) ? nearest.left : nearest.right;
- if (child == null) {
- break;
- }
-
- nearest = child;
- }
- }
-
- // The key doesn't exist in this tree.
- if (!create) {
- return null;
- }
-
- // Create the node and add it to the tree or the table.
- Node header = this.header;
- Node created;
- if (nearest == null) {
- // Check that the value is comparable if we didn't do any comparisons.
- if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
- throw new ClassCastException(key.getClass().getName() + " is not Comparable");
- }
- created = new Node(nearest, key, hash, header, header.prev);
- table[index] = created;
- } else {
- created = new Node(nearest, key, hash, header, header.prev);
- if (comparison < 0) { // nearest.key is higher
- nearest.left = created;
- } else { // comparison > 0, nearest.key is lower
- nearest.right = created;
- }
- rebalance(nearest, true);
- }
-
- if (size++ > threshold) {
- doubleCapacity();
- }
- modCount++;
-
- return created;
- }
-
- @SuppressWarnings("unchecked")
- Node findByObject(Object key) {
- try {
- return key != null ? find((K) key, false) : null;
- } catch (ClassCastException e) {
- return null;
- }
- }
-
- /**
- * Returns this map's entry that has the same key and value as {@code
- * entry}, or null if this map has no such entry.
- *
- * This method uses the comparator for key equality rather than {@code
- * equals}. If this map's comparator isn't consistent with equals (such as
- * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
- * contains()} will violate the collections API.
- */
- Node findByEntry(Entry, ?> entry) {
- Node mine = findByObject(entry.getKey());
- boolean valuesEqual = mine != null && equal(mine.value, entry.getValue());
- return valuesEqual ? mine : null;
- }
-
- private boolean equal(Object a, Object b) {
- return a == b || (a != null && a.equals(b));
- }
-
- /**
- * Applies a supplemental hash function to a given hashCode, which defends
- * against poor quality hash functions. This is critical because HashMap
- * uses power-of-two length hash tables, that otherwise encounter collisions
- * for hashCodes that do not differ in lower or upper bits.
- */
- private static int secondaryHash(int h) {
- // Doug Lea's supplemental hash function
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
-
- /**
- * Removes {@code node} from this tree, rearranging the tree's structure as
- * necessary.
- *
- * @param unlink true to also unlink this node from the iteration linked list.
- */
- void removeInternal(Node node, boolean unlink) {
- if (unlink) {
- node.prev.next = node.next;
- node.next.prev = node.prev;
- node.next = node.prev = null; // Help the GC (for performance)
- }
-
- Node left = node.left;
- Node right = node.right;
- Node originalParent = node.parent;
- if (left != null && right != null) {
-
- /*
- * To remove a node with both left and right subtrees, move an
- * adjacent node from one of those subtrees into this node's place.
- *
- * Removing the adjacent node may change this node's subtrees. This
- * node may no longer have two subtrees once the adjacent node is
- * gone!
- */
-
- Node adjacent = (left.height > right.height) ? left.last() : right.first();
- removeInternal(adjacent, false); // takes care of rebalance and size--
-
- int leftHeight = 0;
- left = node.left;
- if (left != null) {
- leftHeight = left.height;
- adjacent.left = left;
- left.parent = adjacent;
- node.left = null;
- }
- int rightHeight = 0;
- right = node.right;
- if (right != null) {
- rightHeight = right.height;
- adjacent.right = right;
- right.parent = adjacent;
- node.right = null;
- }
- adjacent.height = Math.max(leftHeight, rightHeight) + 1;
- replaceInParent(node, adjacent);
- return;
- } else if (left != null) {
- replaceInParent(node, left);
- node.left = null;
- } else if (right != null) {
- replaceInParent(node, right);
- node.right = null;
- } else {
- replaceInParent(node, null);
- }
-
- rebalance(originalParent, false);
- size--;
- modCount++;
- }
-
- Node removeInternalByKey(Object key) {
- Node node = findByObject(key);
- if (node != null) {
- removeInternal(node, true);
- }
- return node;
- }
-
- private void replaceInParent(Node node, Node replacement) {
- Node parent = node.parent;
- node.parent = null;
- if (replacement != null) {
- replacement.parent = parent;
- }
-
- if (parent != null) {
- if (parent.left == node) {
- parent.left = replacement;
- } else {
- assert (parent.right == node);
- parent.right = replacement;
- }
- } else {
- int index = node.hash & (table.length - 1);
- table[index] = replacement;
- }
- }
-
- /**
- * Rebalances the tree by making any AVL rotations necessary between the
- * newly-unbalanced node and the tree's root.
- *
- * @param insert true if the node was unbalanced by an insert; false if it
- * was by a removal.
- */
- private void rebalance(Node unbalanced, boolean insert) {
- for (Node node = unbalanced; node != null; node = node.parent) {
- Node left = node.left;
- Node right = node.right;
- int leftHeight = left != null ? left.height : 0;
- int rightHeight = right != null ? right.height : 0;
-
- int delta = leftHeight - rightHeight;
- if (delta == -2) {
- Node rightLeft = right.left;
- Node rightRight = right.right;
- int rightRightHeight = rightRight != null ? rightRight.height : 0;
- int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
-
- int rightDelta = rightLeftHeight - rightRightHeight;
- if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
- rotateLeft(node); // AVL right right
- } else {
- assert (rightDelta == 1);
- rotateRight(right); // AVL right left
- rotateLeft(node);
- }
- if (insert) {
- break; // no further rotations will be necessary
- }
-
- } else if (delta == 2) {
- Node leftLeft = left.left;
- Node leftRight = left.right;
- int leftRightHeight = leftRight != null ? leftRight.height : 0;
- int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
-
- int leftDelta = leftLeftHeight - leftRightHeight;
- if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
- rotateRight(node); // AVL left left
- } else {
- assert (leftDelta == -1);
- rotateLeft(left); // AVL left right
- rotateRight(node);
- }
- if (insert) {
- break; // no further rotations will be necessary
- }
-
- } else if (delta == 0) {
- node.height = leftHeight + 1; // leftHeight == rightHeight
- if (insert) {
- break; // the insert caused balance, so rebalancing is done!
- }
-
- } else {
- assert (delta == -1 || delta == 1);
- node.height = Math.max(leftHeight, rightHeight) + 1;
- if (!insert) {
- break; // the height hasn't changed, so rebalancing is done!
- }
- }
- }
- }
-
- /**
- * Rotates the subtree so that its root's right child is the new root.
- */
- private void rotateLeft(Node root) {
- Node left = root.left;
- Node pivot = root.right;
- Node pivotLeft = pivot.left;
- Node pivotRight = pivot.right;
-
- // move the pivot's left child to the root's right
- root.right = pivotLeft;
- if (pivotLeft != null) {
- pivotLeft.parent = root;
- }
-
- replaceInParent(root, pivot);
-
- // move the root to the pivot's left
- pivot.left = root;
- root.parent = pivot;
-
- // fix heights
- root.height = Math.max(left != null ? left.height : 0,
- pivotLeft != null ? pivotLeft.height : 0) + 1;
- pivot.height = Math.max(root.height,
- pivotRight != null ? pivotRight.height : 0) + 1;
- }
-
- /**
- * Rotates the subtree so that its root's left child is the new root.
- */
- private void rotateRight(Node root) {
- Node pivot = root.left;
- Node right = root.right;
- Node pivotLeft = pivot.left;
- Node pivotRight = pivot.right;
-
- // move the pivot's right child to the root's left
- root.left = pivotRight;
- if (pivotRight != null) {
- pivotRight.parent = root;
- }
-
- replaceInParent(root, pivot);
-
- // move the root to the pivot's right
- pivot.right = root;
- root.parent = pivot;
-
- // fixup heights
- root.height = Math.max(right != null ? right.height : 0,
- pivotRight != null ? pivotRight.height : 0) + 1;
- pivot.height = Math.max(root.height,
- pivotLeft != null ? pivotLeft.height : 0) + 1;
- }
-
- private EntrySet entrySet;
- private KeySet keySet;
-
- @Override public Set> entrySet() {
- EntrySet result = entrySet;
- return result != null ? result : (entrySet = new EntrySet());
- }
-
- @Override public Set keySet() {
- KeySet result = keySet;
- return result != null ? result : (keySet = new KeySet());
- }
-
- static final class Node implements Entry {
- Node parent;
- Node left;
- Node right;
- Node next;
- Node prev;
- final K key;
- final int hash;
- V value;
- int height;
-
- /** Create the header entry */
- Node() {
- key = null;
- hash = -1;
- next = prev = this;
- }
-
- /** Create a regular entry */
- Node(Node parent, K key, int hash, Node next, Node prev) {
- this.parent = parent;
- this.key = key;
- this.hash = hash;
- this.height = 1;
- this.next = next;
- this.prev = prev;
- prev.next = this;
- next.prev = this;
- }
-
- public K getKey() {
- return key;
- }
-
- public V getValue() {
- return value;
- }
-
- public V setValue(V value) {
- V oldValue = this.value;
- this.value = value;
- return oldValue;
- }
-
- @SuppressWarnings("rawtypes")
- @Override public boolean equals(Object o) {
- if (o instanceof Entry) {
- Entry other = (Entry) o;
- return (key == null ? other.getKey() == null : key.equals(other.getKey()))
- && (value == null ? other.getValue() == null : value.equals(other.getValue()));
- }
- return false;
- }
-
- @Override public int hashCode() {
- return (key == null ? 0 : key.hashCode())
- ^ (value == null ? 0 : value.hashCode());
- }
-
- @Override public String toString() {
- return key + "=" + value;
- }
-
- /**
- * Returns the first node in this subtree.
- */
- public Node first() {
- Node node = this;
- Node child = node.left;
- while (child != null) {
- node = child;
- child = node.left;
- }
- return node;
- }
-
- /**
- * Returns the last node in this subtree.
- */
- public Node last() {
- Node node = this;
- Node child = node.right;
- while (child != null) {
- node = child;
- child = node.right;
- }
- return node;
- }
- }
-
- private void doubleCapacity() {
- table = doubleCapacity(table);
- threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
- }
-
- /**
- * Returns a new array containing the same nodes as {@code oldTable}, but with
- * twice as many trees, each of (approximately) half the previous size.
- */
- static Node[] doubleCapacity(Node[] oldTable) {
- // TODO: don't do anything if we're already at MAX_CAPACITY
- int oldCapacity = oldTable.length;
- @SuppressWarnings("unchecked") // Arrays and generics don't get along.
- Node[] newTable = new Node[oldCapacity * 2];
- AvlIterator iterator = new AvlIterator();
- AvlBuilder leftBuilder = new AvlBuilder();
- AvlBuilder rightBuilder = new AvlBuilder();
-
- // Split each tree into two trees.
- for (int i = 0; i < oldCapacity; i++) {
- Node root = oldTable[i];
- if (root == null) {
- continue;
- }
-
- // Compute the sizes of the left and right trees.
- iterator.reset(root);
- int leftSize = 0;
- int rightSize = 0;
- for (Node node; (node = iterator.next()) != null; ) {
- if ((node.hash & oldCapacity) == 0) {
- leftSize++;
- } else {
- rightSize++;
- }
- }
-
- // Split the tree into two.
- leftBuilder.reset(leftSize);
- rightBuilder.reset(rightSize);
- iterator.reset(root);
- for (Node node; (node = iterator.next()) != null; ) {
- if ((node.hash & oldCapacity) == 0) {
- leftBuilder.add(node);
- } else {
- rightBuilder.add(node);
- }
- }
-
- // Populate the enlarged array with these new roots.
- newTable[i] = leftSize > 0 ? leftBuilder.root() : null;
- newTable[i + oldCapacity] = rightSize > 0 ? rightBuilder.root() : null;
- }
- return newTable;
- }
-
- /**
- * Walks an AVL tree in iteration order. Once a node has been returned, its
- * left, right and parent links are no longer used . For this
- * reason it is safe to transform these links as you walk a tree.
- *
- * Warning: this iterator is destructive. It clears the
- * parent node of all nodes in the tree. It is an error to make a partial
- * iteration of a tree.
- */
- static class AvlIterator {
- /** This stack is a singly linked list, linked by the 'parent' field. */
- private Node stackTop;
-
- void reset(Node root) {
- Node stackTop = null;
- for (Node n = root; n != null; n = n.left) {
- n.parent = stackTop;
- stackTop = n; // Stack push.
- }
- this.stackTop = stackTop;
- }
-
- public Node next() {
- Node stackTop = this.stackTop;
- if (stackTop == null) {
- return null;
- }
- Node result = stackTop;
- stackTop = result.parent;
- result.parent = null;
- for (Node n = result.right; n != null; n = n.left) {
- n.parent = stackTop;
- stackTop = n; // Stack push.
- }
- this.stackTop = stackTop;
- return result;
- }
- }
-
- /**
- * Builds AVL trees of a predetermined size by accepting nodes of increasing
- * value. To use:
- *
- * Call {@link #reset} to initialize the target size size .
- * Call {@link #add} size times with increasing values.
- * Call {@link #root} to get the root of the balanced tree.
- *
- *
- * The returned tree will satisfy the AVL constraint: for every node
- * N , the height of N.left and N.right is different by at
- * most 1. It accomplishes this by omitting deepest-level leaf nodes when
- * building trees whose size isn't a power of 2 minus 1.
- *
- *
Unlike rebuilding a tree from scratch, this approach requires no value
- * comparisons. Using this class to create a tree of size S is
- * {@code O(S)}.
- */
- final static class AvlBuilder {
- /** This stack is a singly linked list, linked by the 'parent' field. */
- private Node stack;
- private int leavesToSkip;
- private int leavesSkipped;
- private int size;
-
- void reset(int targetSize) {
- // compute the target tree size. This is a power of 2 minus one, like 15 or 31.
- int treeCapacity = Integer.highestOneBit(targetSize) * 2 - 1;
- leavesToSkip = treeCapacity - targetSize;
- size = 0;
- leavesSkipped = 0;
- stack = null;
- }
-
- void add(Node node) {
- node.left = node.parent = node.right = null;
- node.height = 1;
-
- // Skip a leaf if necessary.
- if (leavesToSkip > 0 && (size & 1) == 0) {
- size++;
- leavesToSkip--;
- leavesSkipped++;
- }
-
- node.parent = stack;
- stack = node; // Stack push.
- size++;
-
- // Skip a leaf if necessary.
- if (leavesToSkip > 0 && (size & 1) == 0) {
- size++;
- leavesToSkip--;
- leavesSkipped++;
- }
-
- /*
- * Combine 3 nodes into subtrees whenever the size is one less than a
- * multiple of 4. For example we combine the nodes A, B, C into a
- * 3-element tree with B as the root.
- *
- * Combine two subtrees and a spare single value whenever the size is one
- * less than a multiple of 8. For example at 8 we may combine subtrees
- * (A B C) and (E F G) with D as the root to form ((A B C) D (E F G)).
- *
- * Just as we combine single nodes when size nears a multiple of 4, and
- * 3-element trees when size nears a multiple of 8, we combine subtrees of
- * size (N-1) whenever the total size is 2N-1 whenever N is a power of 2.
- */
- for (int scale = 4; (size & scale - 1) == scale - 1; scale *= 2) {
- if (leavesSkipped == 0) {
- // Pop right, center and left, then make center the top of the stack.
- Node right = stack;
- Node center = right.parent;
- Node left = center.parent;
- center.parent = left.parent;
- stack = center;
- // Construct a tree.
- center.left = left;
- center.right = right;
- center.height = right.height + 1;
- left.parent = center;
- right.parent = center;
- } else if (leavesSkipped == 1) {
- // Pop right and center, then make center the top of the stack.
- Node right = stack;
- Node center = right.parent;
- stack = center;
- // Construct a tree with no left child.
- center.right = right;
- center.height = right.height + 1;
- right.parent = center;
- leavesSkipped = 0;
- } else if (leavesSkipped == 2) {
- leavesSkipped = 0;
- }
- }
- }
-
- Node root() {
- Node stackTop = this.stack;
- if (stackTop.parent != null) {
- throw new IllegalStateException();
- }
- return stackTop;
- }
- }
-
- private abstract class LinkedTreeMapIterator implements Iterator {
- Node next = header.next;
- Node lastReturned = null;
- int expectedModCount = modCount;
-
- LinkedTreeMapIterator() {
- }
-
- public final boolean hasNext() {
- return next != header;
- }
-
- final Node nextNode() {
- Node e = next;
- if (e == header) {
- throw new NoSuchElementException();
- }
- if (modCount != expectedModCount) {
- throw new ConcurrentModificationException();
- }
- next = e.next;
- return lastReturned = e;
- }
-
- public final void remove() {
- if (lastReturned == null) {
- throw new IllegalStateException();
- }
- removeInternal(lastReturned, true);
- lastReturned = null;
- expectedModCount = modCount;
- }
- }
-
- final class EntrySet extends AbstractSet> {
- @Override public int size() {
- return size;
- }
-
- @Override public Iterator> iterator() {
- return new LinkedTreeMapIterator>() {
- public Entry next() {
- return nextNode();
- }
- };
- }
-
- @Override public boolean contains(Object o) {
- return o instanceof Entry && findByEntry((Entry, ?>) o) != null;
- }
-
- @Override public boolean remove(Object o) {
- if (!(o instanceof Entry)) {
- return false;
- }
-
- Node node = findByEntry((Entry, ?>) o);
- if (node == null) {
- return false;
- }
- removeInternal(node, true);
- return true;
- }
-
- @Override public void clear() {
- LinkedHashTreeMap.this.clear();
- }
- }
-
- final class KeySet extends AbstractSet {
- @Override public int size() {
- return size;
- }
-
- @Override public Iterator iterator() {
- return new LinkedTreeMapIterator() {
- public K next() {
- return nextNode().key;
- }
- };
- }
-
- @Override public boolean contains(Object o) {
- return containsKey(o);
- }
-
- @Override public boolean remove(Object key) {
- return removeInternalByKey(key) != null;
- }
-
- @Override public void clear() {
- LinkedHashTreeMap.this.clear();
- }
- }
-
- /**
- * If somebody is unlucky enough to have to serialize one of these, serialize
- * it as a LinkedHashMap so that they won't need Gson on the other side to
- * deserialize it. Using serialization defeats our DoS defence, so most apps
- * shouldn't use it.
- */
- private Object writeReplace() throws ObjectStreamException {
- return new LinkedHashMap(this);
- }
-
- private void readObject(ObjectInputStream in) throws IOException {
- // Don't permit directly deserializing this class; writeReplace() should have written a replacement
- throw new InvalidObjectException("Deserialization is unsupported");
- }
-}
diff --git a/gson/src/test/java/com/google/gson/internal/LinkedHashTreeMapTest.java b/gson/src/test/java/com/google/gson/internal/LinkedHashTreeMapTest.java
deleted file mode 100644
index 65864f0c..00000000
--- a/gson/src/test/java/com/google/gson/internal/LinkedHashTreeMapTest.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- *
- * Licensed 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 com.google.gson.internal;
-
-import com.google.gson.common.MoreAsserts;
-import com.google.gson.internal.LinkedHashTreeMap.AvlBuilder;
-import com.google.gson.internal.LinkedHashTreeMap.AvlIterator;
-import com.google.gson.internal.LinkedHashTreeMap.Node;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Random;
-import junit.framework.TestCase;
-
-public final class LinkedHashTreeMapTest extends TestCase {
- public void testIterationOrder() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- map.put("a", "android");
- map.put("c", "cola");
- map.put("b", "bbq");
- assertIterationOrder(map.keySet(), "a", "c", "b");
- assertIterationOrder(map.values(), "android", "cola", "bbq");
- }
-
- public void testRemoveRootDoesNotDoubleUnlink() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- map.put("a", "android");
- map.put("c", "cola");
- map.put("b", "bbq");
- Iterator> it = map.entrySet().iterator();
- it.next();
- it.next();
- it.next();
- it.remove();
- assertIterationOrder(map.keySet(), "a", "c");
- }
-
- public void testPutNullKeyFails() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- try {
- map.put(null, "android");
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testPutNonComparableKeyFails() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- try {
- map.put(new Object(), "android");
- fail();
- } catch (ClassCastException expected) {}
- }
-
- public void testContainsNonComparableKeyReturnsFalse() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- map.put("a", "android");
- assertFalse(map.containsKey(new Object()));
- }
-
- public void testContainsNullKeyIsAlwaysFalse() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- map.put("a", "android");
- assertFalse(map.containsKey(null));
- }
-
- public void testPutOverrides() throws Exception {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- assertNull(map.put("d", "donut"));
- assertNull(map.put("e", "eclair"));
- assertNull(map.put("f", "froyo"));
- assertEquals(3, map.size());
-
- assertEquals("donut", map.get("d"));
- assertEquals("donut", map.put("d", "done"));
- assertEquals(3, map.size());
- }
-
- public void testEmptyStringValues() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- map.put("a", "");
- assertTrue(map.containsKey("a"));
- assertEquals("", map.get("a"));
- }
-
- // NOTE that this does not happen every time, but given the below predictable random,
- // this test will consistently fail (assuming the initial size is 16 and rehashing
- // size remains at 3/4)
- public void testForceDoublingAndRehash() throws Exception {
- Random random = new Random(1367593214724L);
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- String[] keys = new String[1000];
- for (int i = 0; i < keys.length; i++) {
- keys[i] = Integer.toString(Math.abs(random.nextInt()), 36) + "-" + i;
- map.put(keys[i], "" + i);
- }
-
- for (int i = 0; i < keys.length; i++) {
- String key = keys[i];
- assertTrue(map.containsKey(key));
- assertEquals("" + i, map.get(key));
- }
- }
-
- public void testClear() {
- LinkedHashTreeMap map = new LinkedHashTreeMap();
- map.put("a", "android");
- map.put("c", "cola");
- map.put("b", "bbq");
- map.clear();
- assertIterationOrder(map.keySet());
- assertEquals(0, map.size());
- }
-
- public void testEqualsAndHashCode() throws Exception {
- LinkedHashTreeMap map1 = new LinkedHashTreeMap();
- map1.put("A", 1);
- map1.put("B", 2);
- map1.put("C", 3);
- map1.put("D", 4);
-
- LinkedHashTreeMap map2 = new LinkedHashTreeMap();
- map2.put("C", 3);
- map2.put("B", 2);
- map2.put("D", 4);
- map2.put("A", 1);
-
- MoreAsserts.assertEqualsAndHashCode(map1, map2);
- }
-
- public void testAvlWalker() {
- assertAvlWalker(node(node("a"), "b", node("c")),
- "a", "b", "c");
- assertAvlWalker(node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g"))),
- "a", "b", "c", "d", "e", "f", "g");
- assertAvlWalker(node(node(null, "a", node("b")), "c", node(node("d"), "e", null)),
- "a", "b", "c", "d", "e");
- assertAvlWalker(node(null, "a", node(null, "b", node(null, "c", node("d")))),
- "a", "b", "c", "d");
- assertAvlWalker(node(node(node(node("a"), "b", null), "c", null), "d", null),
- "a", "b", "c", "d");
- }
-
- private void assertAvlWalker(Node root, String... values) {
- AvlIterator iterator = new AvlIterator();
- iterator.reset(root);
- for (String value : values) {
- assertEquals(value, iterator.next().getKey());
- }
- assertNull(iterator.next());
- }
-
- public void testAvlBuilder() {
- assertAvlBuilder(1, "a");
- assertAvlBuilder(2, "(. a b)");
- assertAvlBuilder(3, "(a b c)");
- assertAvlBuilder(4, "(a b (. c d))");
- assertAvlBuilder(5, "(a b (c d e))");
- assertAvlBuilder(6, "((. a b) c (d e f))");
- assertAvlBuilder(7, "((a b c) d (e f g))");
- assertAvlBuilder(8, "((a b c) d (e f (. g h)))");
- assertAvlBuilder(9, "((a b c) d (e f (g h i)))");
- assertAvlBuilder(10, "((a b c) d ((. e f) g (h i j)))");
- assertAvlBuilder(11, "((a b c) d ((e f g) h (i j k)))");
- assertAvlBuilder(12, "((a b (. c d)) e ((f g h) i (j k l)))");
- assertAvlBuilder(13, "((a b (c d e)) f ((g h i) j (k l m)))");
- assertAvlBuilder(14, "(((. a b) c (d e f)) g ((h i j) k (l m n)))");
- assertAvlBuilder(15, "(((a b c) d (e f g)) h ((i j k) l (m n o)))");
- assertAvlBuilder(16, "(((a b c) d (e f g)) h ((i j k) l (m n (. o p))))");
- assertAvlBuilder(30, "((((. a b) c (d e f)) g ((h i j) k (l m n))) o "
- + "(((p q r) s (t u v)) w ((x y z) A (B C D))))");
- assertAvlBuilder(31, "((((a b c) d (e f g)) h ((i j k) l (m n o))) p "
- + "(((q r s) t (u v w)) x ((y z A) B (C D E))))");
- }
-
- private void assertAvlBuilder(int size, String expected) {
- char[] values = "abcdefghijklmnopqrstuvwxyzABCDE".toCharArray();
- AvlBuilder avlBuilder = new AvlBuilder();
- avlBuilder.reset(size);
- for (int i = 0; i < size; i++) {
- avlBuilder.add(node(Character.toString(values[i])));
- }
- assertTree(expected, avlBuilder.root());
- }
-
- public void testDoubleCapacity() {
- @SuppressWarnings("unchecked") // Arrays and generics don't get along.
- Node[] oldTable = new Node[1];
- oldTable[0] = node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g")));
-
- Node[] newTable = LinkedHashTreeMap.doubleCapacity(oldTable);
- assertTree("(b d f)", newTable[0]); // Even hash codes!
- assertTree("(a c (. e g))", newTable[1]); // Odd hash codes!
- }
-
- public void testDoubleCapacityAllNodesOnLeft() {
- @SuppressWarnings("unchecked") // Arrays and generics don't get along.
- Node[] oldTable = new Node[1];
- oldTable[0] = node(node("b"), "d", node("f"));
-
- Node[] newTable = LinkedHashTreeMap.doubleCapacity(oldTable);
- assertTree("(b d f)", newTable[0]); // Even hash codes!
- assertNull(newTable[1]); // Odd hash codes!
-
- for (Node, ?> node : newTable) {
- if (node != null) {
- assertConsistent(node);
- }
- }
- }
-
- public void testJavaSerialization() throws IOException, ClassNotFoundException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ObjectOutputStream objOut = new ObjectOutputStream(out);
- Map map = new LinkedHashTreeMap();
- map.put("a", 1);
- objOut.writeObject(map);
- objOut.close();
-
- ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
- @SuppressWarnings("unchecked")
- Map deserialized = (Map) objIn.readObject();
- assertEquals(Collections.singletonMap("a", 1), deserialized);
- }
-
- private static final Node head = new Node();
-
- private Node node(String value) {
- return new Node(null, value, value.hashCode(), head, head);
- }
-
- private Node node(Node left, String value,
- Node right) {
- Node result = node(value);
- if (left != null) {
- result.left = left;
- left.parent = result;
- }
- if (right != null) {
- result.right = right;
- right.parent = result;
- }
- return result;
- }
-
- private void assertTree(String expected, Node, ?> root) {
- assertEquals(expected, toString(root));
- assertConsistent(root);
- }
-
- private void assertConsistent(Node, ?> node) {
- int leftHeight = 0;
- if (node.left != null) {
- assertConsistent(node.left);
- assertSame(node, node.left.parent);
- leftHeight = node.left.height;
- }
- int rightHeight = 0;
- if (node.right != null) {
- assertConsistent(node.right);
- assertSame(node, node.right.parent);
- rightHeight = node.right.height;
- }
- if (node.parent != null) {
- assertTrue(node.parent.left == node || node.parent.right == node);
- }
- if (Math.max(leftHeight, rightHeight) + 1 != node.height) {
- fail();
- }
- }
-
- private String toString(Node, ?> root) {
- if (root == null) {
- return ".";
- } else if (root.left == null && root.right == null) {
- return String.valueOf(root.key);
- } else {
- return String.format("(%s %s %s)", toString(root.left), root.key, toString(root.right));
- }
- }
-
- @SafeVarargs
- private void assertIterationOrder(Iterable actual, T... expected) {
- ArrayList actualList = new ArrayList();
- for (T t : actual) {
- actualList.add(t);
- }
- assertEquals(Arrays.asList(expected), actualList);
- }
-}