All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.emul.java.util.AbstractHashMap Maven / Gradle / Ivy

/*
 * Copyright 2008 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 java.util;

import static java.util.ConcurrentModificationDetector.checkStructuralChange;
import static java.util.ConcurrentModificationDetector.recordLastKnownStructure;
import static java.util.ConcurrentModificationDetector.structureChanged;

import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkElement;
import static javaemul.internal.InternalPreconditions.checkState;

import javaemul.internal.JsUtils;
import javaemul.internal.annotations.SpecializeMethod;

/**
 * Implementation of Map interface based on a hash table. [Sun
 * docs]
 *
 * @param  key type
 * @param  value type
 */
abstract class AbstractHashMap extends AbstractMap {

  private final class EntrySet extends AbstractSet> {

    @Override
    public void clear() {
      AbstractHashMap.this.clear();
    }

    @Override
    public boolean contains(Object o) {
      if (o instanceof Map.Entry) {
        return containsEntry((Map.Entry) o);
      }
      return false;
    }

    @Override
    public Iterator> iterator() {
      return new EntrySetIterator();
    }

    @Override
    public boolean remove(Object entry) {
      if (contains(entry)) {
        Object key = ((Map.Entry) entry).getKey();
        AbstractHashMap.this.remove(key);
        return true;
      }
      return false;
    }

    @Override
    public int size() {
      return AbstractHashMap.this.size();
    }
  }

  /**
   * Iterator for EntrySet.
   */
  private final class EntrySetIterator implements Iterator> {
    private Iterator> stringMapEntries = stringMap.iterator();
    private Iterator> current = stringMapEntries;
    private Iterator> last;
    private boolean hasNext = computeHasNext();

    public EntrySetIterator() {
      recordLastKnownStructure(AbstractHashMap.this, this);
    }

    @Override
    public boolean hasNext() {
      return hasNext;
    }

    private boolean computeHasNext() {
      if (current.hasNext()) {
        return true;
      }
      if (current != stringMapEntries) {
        return false;
      }
      current = hashCodeMap.iterator();
      return current.hasNext();
    }

    @Override
    public Entry next() {
      checkStructuralChange(AbstractHashMap.this, this);
      checkElement(hasNext());

      last = current;
      Entry rv = current.next();
      hasNext = computeHasNext();

      return rv;
    }

    @Override
    public void remove() {
      checkState(last != null);
      checkStructuralChange(AbstractHashMap.this, this);

      last.remove();
      last = null;
      hasNext = computeHasNext();

      recordLastKnownStructure(AbstractHashMap.this, this);
    }
  }

  /**
   * A map of integral hashCodes onto entries.
   */
  private transient InternalHashCodeMap hashCodeMap;

  /**
   * A map of Strings onto values.
   */
  private transient InternalStringMap stringMap;

  public AbstractHashMap() {
    reset();
  }

  public AbstractHashMap(int ignored) {
    // This implementation of HashMap has no need of initial capacities.
    this(ignored, 0);
  }

  public AbstractHashMap(int ignored, float alsoIgnored) {
    // This implementation of HashMap has no need of load factors or capacities.
    checkArgument(ignored >= 0, "Negative initial capacity");
    checkArgument(alsoIgnored >= 0, "Non-positive load factor");

    reset();
  }

  public AbstractHashMap(Map toBeCopied) {
    reset();
    this.putAll(toBeCopied);
  }

  @Override
  public void clear() {
    reset();
  }

  private void reset() {
    hashCodeMap = new InternalHashCodeMap(this);
    stringMap = new InternalStringMap(this);
    structureChanged(this);
  }

  @SpecializeMethod(params = {String.class}, target = "hasStringValue")
  @Override
  public boolean containsKey(Object key) {
    return key instanceof String
        ? hasStringValue(JsUtils.uncheckedCast(key)) : hasHashValue(key);
  }

  @Override
  public boolean containsValue(Object value) {
    return containsValue(value, stringMap) || containsValue(value, hashCodeMap);
  }

  private boolean containsValue(Object value, Iterable> entries) {
    for (Entry entry : entries) {
      if (equals(value, entry.getValue())) {
        return true;
      }
    }
    return false;
  }

  @Override
  public Set> entrySet() {
    return new EntrySet();
  }

  @SpecializeMethod(params = {String.class}, target = "getStringValue")
  @Override
  public V get(Object key) {
    return key instanceof String
        ? getStringValue(JsUtils.uncheckedCast(key)) : getHashValue(key);
  }

  @SpecializeMethod(params = {String.class, Object.class}, target = "putStringValue")
  @Override
  public V put(K key, V value) {
    return key instanceof String
        ? putStringValue(JsUtils.uncheckedCast(key), value) : putHashValue(key, value);
  }

  @SpecializeMethod(params = {String.class}, target = "removeStringValue")
  @Override
  public V remove(Object key) {
    return key instanceof String
        ? removeStringValue(JsUtils.uncheckedCast(key)) : removeHashValue(key);
  }

  @Override
  public int size() {
    return hashCodeMap.size() + stringMap.size();
  }

  /**
   * Subclasses must override to return a whether or not two keys or values are
   * equal.
   */
  abstract boolean equals(Object value1, Object value2);

  /**
   * Subclasses must override to return a hash code for a given key. The key is
   * guaranteed to be non-null and not a String.
   */
  abstract int getHashCode(Object key);

  /**
   * Returns the Map.Entry whose key is Object equal to key,
   * provided that key's hash code is hashCode;
   * or null if no such Map.Entry exists at the specified
   * hashCode.
   */
  private V getHashValue(Object key) {
    return getEntryValueOrNull(hashCodeMap.getEntry(key));
  }

  /**
   * Returns the value for the given key in the stringMap. Returns
   * null if the specified key does not exist.
   */
  private V getStringValue(String key) {
    return key == null ? getHashValue(null) : stringMap.get(key);
  }

  /**
   * Returns true if the a key exists in the hashCodeMap that is Object equal to
   * key, provided that key's hash code is
   * hashCode.
   */
  private boolean hasHashValue(Object key) {
    return hashCodeMap.getEntry(key) != null;
  }

  /**
   * Returns true if the given key exists in the stringMap.
   */
  private boolean hasStringValue(String key) {
    return key == null ? hasHashValue(null) : stringMap.contains(key);
  }

  /**
   * Sets the specified key to the specified value in the hashCodeMap. Returns
   * the value previously at that key. Returns null if the
   * specified key did not exist.
   */
  private V putHashValue(K key, V value) {
    return hashCodeMap.put(key, value);
  }

  /**
   * Sets the specified key to the specified value in the stringMap. Returns the
   * value previously at that key. Returns null if the specified
   * key did not exist.
   */
  private V putStringValue(String key, V value) {
    return key == null ? putHashValue(null, value) : stringMap.put(key, value);
  }

  /**
   * Removes the pair whose key is Object equal to key from
   * hashCodeMap, provided that key's hash code
   * is hashCode. Returns the value that was associated with the
   * removed key, or null if no such key existed.
   */
  private V removeHashValue(Object key) {
    return hashCodeMap.remove(key);
  }

  /**
   * Removes the specified key from the stringMap and returns the value that was
   * previously there. Returns null if the specified key does not
   * exist.
   */
  private V removeStringValue(String key) {
    return key == null ? removeHashValue(null) : stringMap.remove(key);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy