31 package com.google.protobuf;
33 import java.util.AbstractMap;
34 import java.util.AbstractSet;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Iterator;
38 import java.util.List;
40 import java.util.NoSuchElementException;
42 import java.util.SortedMap;
43 import java.util.TreeMap;
83 class SmallSortedMap<
K extends Comparable<K>, V> extends AbstractMap<K, V> {
93 static <FieldDescriptorType extends FieldSet.FieldDescriptorLite<FieldDescriptorType>>
94 SmallSortedMap<FieldDescriptorType, Object> newFieldMap(
int arraySize) {
95 return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) {
97 @SuppressWarnings(
"unchecked")
98 public
void makeImmutable() {
100 for (
int i = 0;
i < getNumArrayEntries();
i++) {
101 final Map.Entry<FieldDescriptorType, Object> entry = getArrayEntryAt(
i);
102 if (entry.getKey().isRepeated()) {
103 final List
value = (List) entry.getValue();
104 entry.setValue(Collections.unmodifiableList(
value));
107 for (
Map.Entry<FieldDescriptorType, Object> entry : getOverflowEntries()) {
108 if (entry.getKey().isRepeated()) {
109 final List
value = (List) entry.getValue();
110 entry.setValue(Collections.unmodifiableList(
value));
114 super.makeImmutable();
125 static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest(
int arraySize) {
126 return new SmallSortedMap<K, V>(arraySize);
129 private final int maxArraySize;
133 private List<Entry> entryList;
135 private boolean isImmutable;
138 private volatile EntrySet lazyEntrySet;
139 private Map<K, V> overflowEntriesDescending;
140 private volatile DescendingEntrySet lazyDescendingEntrySet;
146 private SmallSortedMap(
int arraySize) {
147 this.maxArraySize = arraySize;
148 this.entryList = Collections.emptyList();
149 this.overflowEntries = Collections.emptyMap();
150 this.overflowEntriesDescending = Collections.emptyMap();
154 public void makeImmutable() {
161 overflowEntries.isEmpty()
162 ? Collections.<
K, V>emptyMap()
163 : Collections.unmodifiableMap(overflowEntries);
164 overflowEntriesDescending =
165 overflowEntriesDescending.isEmpty()
166 ? Collections.<
K, V>emptyMap()
167 : Collections.unmodifiableMap(overflowEntriesDescending);
173 public boolean isImmutable() {
178 public int getNumArrayEntries() {
179 return entryList.size();
183 public Map.Entry<
K, V> getArrayEntryAt(
int index) {
184 return entryList.get(
index);
188 public int getNumOverflowEntries() {
189 return overflowEntries.size();
193 public Iterable<
Map.Entry<
K, V>> getOverflowEntries() {
194 return overflowEntries.isEmpty()
195 ? EmptySet.<
Map.Entry<
K, V>>iterable()
196 : overflowEntries.entrySet();
199 Iterable<
Map.Entry<
K, V>> getOverflowEntriesDescending() {
200 return overflowEntriesDescending.isEmpty()
201 ? EmptySet.<
Map.Entry<
K, V>>iterable()
202 : overflowEntriesDescending.entrySet();
207 return entryList.size() + overflowEntries.size();
216 public boolean containsKey(Object o) {
217 @SuppressWarnings(
"unchecked")
219 return binarySearchInArray(
key) >= 0 || overflowEntries.containsKey(
key);
228 public V get(Object o) {
229 @SuppressWarnings(
"unchecked")
231 final
int index = binarySearchInArray(
key);
233 return entryList.get(
index).getValue();
235 return overflowEntries.get(
key);
241 final int index = binarySearchInArray(
key);
246 ensureEntryArrayMutable();
247 final int insertionPoint = -(
index + 1);
248 if (insertionPoint >= maxArraySize) {
250 return getOverflowEntriesMutable().put(
key,
value);
253 if (entryList.size() == maxArraySize) {
255 final Entry lastEntryInArray = entryList.remove(maxArraySize - 1);
256 getOverflowEntriesMutable().put(lastEntryInArray.getKey(), lastEntryInArray.getValue());
258 entryList.add(insertionPoint,
new Entry(
key,
value));
263 public void clear() {
265 if (!entryList.isEmpty()) {
268 if (!overflowEntries.isEmpty()) {
269 overflowEntries.clear();
279 public V
remove(Object o) {
281 @SuppressWarnings(
"unchecked")
283 final
int index = binarySearchInArray(
key);
285 return removeArrayEntryAt(
index);
289 if (overflowEntries.isEmpty()) {
292 return overflowEntries.remove(
key);
296 private V removeArrayEntryAt(
int index) {
298 final V removed = entryList.remove(
index).getValue();
299 if (!overflowEntries.isEmpty()) {
302 final Iterator<
Map.Entry<
K, V>> iterator = getOverflowEntriesMutable().entrySet().iterator();
303 entryList.add(
new Entry(iterator.next()));
314 private int binarySearchInArray(
K key) {
316 int right = entryList.size() - 1;
322 int cmp =
key.compareTo(entryList.get(right).getKey());
325 }
else if (
cmp == 0) {
330 while (
left <= right) {
331 int mid = (
left + right) / 2;
332 int cmp =
key.compareTo(entryList.get(mid).getKey());
335 }
else if (
cmp > 0) {
352 public Set<
Map.Entry<
K, V>> entrySet() {
353 if (lazyEntrySet ==
null) {
354 lazyEntrySet =
new EntrySet();
359 Set<
Map.Entry<
K, V>> descendingEntrySet() {
360 if (lazyDescendingEntrySet ==
null) {
361 lazyDescendingEntrySet =
new DescendingEntrySet();
363 return lazyDescendingEntrySet;
367 private void checkMutable() {
369 throw new UnsupportedOperationException();
377 @SuppressWarnings(
"unchecked")
378 private SortedMap<
K, V> getOverflowEntriesMutable() {
380 if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) {
381 overflowEntries =
new TreeMap<K, V>();
382 overflowEntriesDescending = ((TreeMap<K, V>) overflowEntries).descendingMap();
384 return (SortedMap<K, V>) overflowEntries;
388 private void ensureEntryArrayMutable() {
390 if (entryList.isEmpty() && !(entryList instanceof ArrayList)) {
391 entryList =
new ArrayList<Entry>(maxArraySize);
399 private class Entry
implements Map.Entry<K, V>, Comparable<Entry> {
404 Entry(
Map.Entry<
K, V> copy) {
405 this(copy.getKey(), copy.getValue());
419 public V getValue() {
424 public int compareTo(Entry other) {
425 return getKey().compareTo(other.getKey());
429 public V setValue(V newValue) {
431 final V oldValue = this.
value;
432 this.value = newValue;
437 public boolean equals(Object o) {
441 if (!(o instanceof
Map.Entry)) {
444 @SuppressWarnings(
"unchecked")
445 Map.Entry<?, ?> other = (
Map.Entry<?, ?>) o;
446 return equals(
key, other.getKey()) && equals(
value, other.getValue());
450 public
int hashCode() {
451 return (
key ==
null ? 0 :
key.hashCode()) ^ (
value ==
null ? 0 :
value.hashCode());
455 public String toString() {
460 private boolean equals(Object
o1, Object o2) {
461 return o1 ==
null ? o2 == null :
o1.equals(o2);
466 private class EntrySet
extends AbstractSet<Map.Entry<K, V>> {
469 public Iterator<
Map.Entry<
K, V>> iterator() {
470 return new EntryIterator();
475 return SmallSortedMap.this.size();
484 public boolean contains(Object o) {
485 @SuppressWarnings(
"unchecked")
486 final
Map.Entry<
K, V> entry = (
Map.Entry<
K, V>) o;
487 final V existing = get(entry.getKey());
488 final V
value = entry.getValue();
489 return existing ==
value || (existing != null && existing.equals(
value));
493 public
boolean add(
Map.Entry<
K, V> entry) {
494 if (!contains(entry)) {
495 put(entry.getKey(), entry.getValue());
507 public boolean remove(Object o) {
508 @SuppressWarnings(
"unchecked")
509 final
Map.Entry<
K, V> entry = (
Map.Entry<
K, V>) o;
510 if (contains(entry)) {
511 SmallSortedMap.this.remove(entry.getKey());
518 public void clear() {
519 SmallSortedMap.this.clear();
523 private class DescendingEntrySet
extends EntrySet {
525 public Iterator<
java.util.Map.Entry<
K, V>> iterator() {
526 return new DescendingEntryIterator();
534 private class EntryIterator
implements Iterator<Map.Entry<K, V>> {
536 private int pos = -1;
537 private boolean nextCalledBeforeRemove;
538 private Iterator<
Map.Entry<
K, V>> lazyOverflowIterator;
541 public boolean hasNext() {
542 return (pos + 1) < entryList.size()
543 || (!overflowEntries.isEmpty() && getOverflowIterator().hasNext());
548 nextCalledBeforeRemove =
true;
551 if (++pos < entryList.size()) {
552 return entryList.get(pos);
554 return getOverflowIterator().next();
558 public void remove() {
559 if (!nextCalledBeforeRemove) {
560 throw new IllegalStateException(
"remove() was called before next()");
562 nextCalledBeforeRemove =
false;
565 if (pos < entryList.size()) {
566 removeArrayEntryAt(pos--);
568 getOverflowIterator().remove();
577 private Iterator<
Map.Entry<
K, V>> getOverflowIterator() {
578 if (lazyOverflowIterator ==
null) {
579 lazyOverflowIterator = overflowEntries.entrySet().iterator();
581 return lazyOverflowIterator;
589 private class DescendingEntryIterator
implements Iterator<Map.Entry<K, V>> {
591 private int pos = entryList.size();
592 private Iterator<
Map.Entry<
K, V>> lazyOverflowIterator;
595 public boolean hasNext() {
596 return (pos > 0 && pos <= entryList.size()) || getOverflowIterator().hasNext();
601 if (getOverflowIterator().hasNext()) {
602 return getOverflowIterator().next();
604 return entryList.get(--pos);
608 public void remove() {
609 throw new UnsupportedOperationException();
617 private Iterator<
Map.Entry<
K, V>> getOverflowIterator() {
618 if (lazyOverflowIterator ==
null) {
619 lazyOverflowIterator = overflowEntriesDescending.entrySet().iterator();
621 return lazyOverflowIterator;
630 private static class EmptySet {
632 private static final Iterator<Object> ITERATOR =
633 new Iterator<Object>() {
635 public boolean hasNext() {
640 public Object
next() {
641 throw new NoSuchElementException();
645 public void remove() {
646 throw new UnsupportedOperationException();
650 private static final Iterable<Object> ITERABLE =
651 new Iterable<Object>() {
653 public Iterator<Object> iterator() {
658 @SuppressWarnings(
"unchecked")
659 static <
T> Iterable<
T> iterable() {
660 return (Iterable<T>) ITERABLE;
665 public boolean equals(Object o) {
670 if (!(o instanceof SmallSortedMap)) {
671 return super.equals(o);
674 SmallSortedMap<?, ?> other = (SmallSortedMap<?, ?>) o;
676 if (
size != other.size()) {
681 final int numArrayEntries = getNumArrayEntries();
682 if (numArrayEntries != other.getNumArrayEntries()) {
683 return entrySet().equals(other.entrySet());
686 for (
int i = 0;
i < numArrayEntries;
i++) {
687 if (!getArrayEntryAt(
i).equals(other.getArrayEntryAt(
i))) {
692 if (numArrayEntries !=
size) {
693 return overflowEntries.equals(other.overflowEntries);
700 public int hashCode() {
702 final int listSize = getNumArrayEntries();
703 for (
int i = 0;
i < listSize;
i++) {
704 h += entryList.get(
i).hashCode();
707 if (getNumOverflowEntries() > 0) {
708 h += overflowEntries.hashCode();