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

edu.stanford.nlp.util.CollectionValuedMap Maven / Gradle / Ivy

Go to download

Stanford Parser processes raw text in English, Chinese, German, Arabic, and French, and extracts constituency parse trees.

There is a newer version: 3.9.2
Show newest version
package edu.stanford.nlp.util;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 * Map from keys to {@link Collection}s. Important methods are the {@link #add}
 * and {@link #remove} methods for adding and removing a value to/from the
 * Collection associated with the key, and the {@link #get} method for getting
 * the Collection associated with a key. The class is quite general, because on
 * construction, it is possible to pass a {@link MapFactory} which will be used
 * to create the underlying map and a {@link CollectionFactory} which will be
 * used to create the Collections. Thus this class can be configured to act like
 * a "HashSetValuedMap" or a "ListValuedMap", or even a
 * "HashSetValuedIdentityHashMap". The possibilities are endless!
 * 
 * @param 
 *          Key type of map
 * @param 
 *          Type of the Collection that is the Map's value
 * @author Teg Grenager ([email protected])
 * @author Sarah Spikes ([email protected]) - cleanup and filling in
 *         types
 */
public class CollectionValuedMap implements Map>, Serializable {

  private static final long serialVersionUID = -9064664153962599076l;
  private Map> map;
  protected CollectionFactory cf;
  private boolean treatCollectionsAsImmutable;
  protected MapFactory> mf;

  /**
   * Replaces current Collection mapped to key with the specified Collection.
   * Use carefully!
   * 
   */
  public Collection put(K key, Collection collection) {
    return map.put(key, collection);
  }

  /**
   * Unsupported. Use {@link #addAll(Map)} instead.
   */
  public void putAll(Map> m) {
    throw new UnsupportedOperationException();
  }

  /**
   * The empty collection to be returned when a get doesn't find
   * the key. The collection returned should be empty, such as
   * Collections.emptySet, for example.
   */
  private final Collection emptyValue;

  /**
   * @return the Collection mapped to by key, never null, but may be empty.
   */
  public Collection get(Object key) {
    Collection c = map.get(key);
    if (c == null) {
      c = emptyValue;
    }
    return c;
  }

  /**
   * Adds the value to the Collection mapped to by the key.
   * 
   */
  public void add(K key, V value) {
    if (treatCollectionsAsImmutable) {
      Collection newC = cf.newCollection();
      Collection c = map.get(key);
      if (c != null) {
        newC.addAll(c);
      }
      newC.add(value);
      map.put(key, newC); // replacing the old collection
    } else {
      Collection c = map.get(key);
      if (c == null) {
        c = cf.newCollection();
        map.put(key, c);
      }
      c.add(value); // modifying the old collection
    }
  }
  
  /**
   * Adds the values to the Collection mapped to by the key.
   */
  
  public void addAll(K key, Collection values) {
    if (treatCollectionsAsImmutable) {
      Collection newC = cf.newCollection();
      Collection c = map.get(key);
      if (c != null) {
        newC.addAll(c);
      }
      newC.addAll(values);
      map.put(key, newC); // replacing the old collection
    } else {
      Collection c = map.get(key);
      if (c == null) {
        c = cf.newCollection();
        map.put(key, c);
      }
      c.addAll(values); // modifying the old collection
    }
  }

  // Just add the key (empty collection, but key is in the keySet
  public void addKey(K key) {
    Collection c = map.get(key);
    if (c == null) {
      c = cf.newCollection();
      map.put(key, c);
    }
  }

  /**
   * Adds all of the mappings in m to this CollectionValuedMap. If m is a
   * CollectionValuedMap, it will behave strangely. Use the constructor instead.
   * 
   */
  public void addAll(Map m) {
    if (m instanceof CollectionValuedMap) {
      throw new UnsupportedOperationException();
    }
    for (Map.Entry e : m.entrySet()) {
      add(e.getKey(), e.getValue());
    }
  }

  public void addAll(CollectionValuedMap cvm) {
    for (Entry> entry : cvm.entrySet()) {
      K key = entry.getKey();
      Collection currentCollection = get(key);
      Collection newValues = entry.getValue();
      if (treatCollectionsAsImmutable) {
        Collection newCollection = cf.newCollection();
        if (currentCollection != null) {
          newCollection.addAll(currentCollection);
        }
        newCollection.addAll(newValues);
        map.put(key, newCollection); // replacing the old collection
      } else {
        boolean needToAdd = false;
        if (currentCollection == emptyValue) {
          currentCollection = cf.newCollection();
          needToAdd = true;
        }
        currentCollection.addAll(newValues); // modifying the old collection
        if (needToAdd) {
          map.put(key, currentCollection);
        }
      }
    }
  }

  /**
   * Removes the mapping associated with this key from this Map.
   * 
   * @return the Collection mapped to by this key.
   */
  public Collection remove(Object key) {
    return map.remove(key);
  }

  /**
   * removes the mappings associated with the keys from this map
   * @param keys
   */
  public void removeAll(Collection keys) {
    for (K k : keys) {
      remove(k);
    }
  }

  /**
   * Removes the value from the Collection mapped to by this key, leaving the
   * rest of the collection intact.
   * 
   * @param key
   *          the key to the Collection to remove the value from
   * @param value
   *          the value to remove
   */
  public void removeMapping(K key, V value) {
    if (treatCollectionsAsImmutable) {
      Collection c = map.get(key);
      if (c != null) {
        Collection newC = cf.newCollection();
        newC.addAll(c);
        newC.remove(value);
        map.put(key, newC);
      }

    } else {
      Collection c = get(key);
      c.remove(value);
    }
  }

  /**
   * Clears this Map.
   */
  public void clear() {
    map.clear();
  }

  /**
   * @return true iff this key is in this map
   */
  public boolean containsKey(Object key) {
    return map.containsKey(key);
  }

  /**
   * Unsupported.
   */
  public boolean containsValue(Object value) {
    throw new UnsupportedOperationException();
  }

  /**
   * @return true iff this Map has no mappings in it.
   */
  public boolean isEmpty() {
    return map.isEmpty();
  }

  /**
   * Each element of the Set is a Map.Entry object, where getKey() returns the
   * key of the mapping, and getValue() returns the Collection mapped to by the
   * key.
   * 
   * @return a Set view of the mappings contained in this map.
   */
  public Set>> entrySet() {
    return map.entrySet();
  }

  /**
   * @return a Set view of the keys in this Map.
   */
  public Set keySet() {
    return map.keySet();
  }

  /**
   * The number of keys in this map.
   */
  public int size() {
    return map.size();
  }

  /**
   * @return a collection of the values (really, a collection of values) in this
   *         Map
   */
  public Collection> values() {
    return map.values();
  }

  public Collection allValues() {
    Collection c = cf.newCollection();
    for (Collection c1 : map.values()) {
      c.addAll(c1);
    }
    return c;
  }

  /**
   * @return true iff o is a CollectionValuedMap, and each key maps to the a
   *         Collection of the same objects in o as it does in this
   *         CollectionValuedMap.
   */
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof CollectionValuedMap)) {
      return false;
    }

    CollectionValuedMap other = ErasureUtils.uncheckedCast(o);

    if (other.size() != size()) {
      return false;
    }

    try {
      for (Map.Entry> e : entrySet()) {
        K key = e.getKey();
        Collection value = e.getValue();
        if (value == null) {
          if (!(other.get(key) == null && other.containsKey(key))) {
            return false;
          }
        } else {
          if (!value.equals(other.get(key))) {
            return false;
          }
        }
      }
    } catch (ClassCastException unused) {
      return false;
    } catch (NullPointerException unused) {
      return false;
    }

    return true;
  }

  /**
   * @return the hashcode of the underlying Map
   */
  @Override
  public int hashCode() {
    return map.hashCode();
  }

  /**
   * Creates a "delta clone" of this Map, where only the differences are
   * represented.
   */
  public CollectionValuedMap deltaClone() {
    CollectionValuedMap result = new CollectionValuedMap<>(null, cf, true);
    result.map = new DeltaMap<>(this.map);
    return result;
  }

  /**
   * @return a clone of this Map
   */
  @Override
  public CollectionValuedMap clone() {
    CollectionValuedMap result = new CollectionValuedMap<>(this);
    return result;
  }

  /**
   * @return A String representation of this CollectionValuedMap, with special
   *         machinery to avoid recursion problems
   */
  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder();
    buf.append('{');

    Iterator>> i = entrySet().iterator();
    while (i.hasNext()) {
      Map.Entry> e = i.next();
      K key = e.getKey();
      Collection value = e.getValue();
      buf.append(key == this ? "(this Map)" : key).append('=').append(value == this ? "(this Map)" : value);

      if (i.hasNext()) {
        buf.append(", ");
      }
    }

    buf.append('}');
    return buf.toString();
  }

  /**
   * Creates a new empty CollectionValuedMap.
   * 
   * @param mf
   *          a MapFactory which will be used to generate the underlying Map
   * @param cf
   *          a CollectionFactory which will be used to generate the Collections
   *          in each mapping
   * @param treatCollectionsAsImmutable
   *          if true, forces this Map to create new a Collection everytime a
   *          new value is added to or deleted from the Collection a mapping.
   */
  public CollectionValuedMap(MapFactory> mf, CollectionFactory cf,
      boolean treatCollectionsAsImmutable) {
    this.mf = mf;
    this.cf = cf;
    this.treatCollectionsAsImmutable = treatCollectionsAsImmutable;
    this.emptyValue = cf.newEmptyCollection();
    if (mf != null) {
      map = Collections.synchronizedMap(mf.newMap());
    }
  }

  /**
   * Creates a new CollectionValuedMap with all of the mappings from cvm. Same
   * as {@link #clone()}.
   */
  public CollectionValuedMap(CollectionValuedMap cvm) {
    this.mf = cvm.mf;
    this.cf = cvm.cf;
    this.treatCollectionsAsImmutable = cvm.treatCollectionsAsImmutable;
    this.emptyValue = cvm.emptyValue;
    map = Collections.synchronizedMap(mf.newMap());
    for (Map.Entry> entry : cvm.map.entrySet()) {
      K key = entry.getKey();
      Collection c = entry.getValue();
      for (V value : c) {
        add(key, value);
      }
    }
  }

  /**
   * Creates a new empty CollectionValuedMap which uses a HashMap as the
   * underlying Map, and HashSets as the Collections in each mapping. Does not
   * treat Collections as immutable.
   */
  public CollectionValuedMap() {
    this(MapFactory.> hashMapFactory(), CollectionFactory. hashSetFactory(), false);
  }

  /**
   * Creates a new empty CollectionValuedMap which uses a HashMap as the
   * underlying Map. Does not treat Collections as immutable.
   * 
   * @param cf
   *          a CollectionFactory which will be used to generate the Collections
   *          in each mapping
   */
  public CollectionValuedMap(CollectionFactory cf) {
    this(MapFactory.> hashMapFactory(), cf, false);
  }

  /**
   * For testing only.
   * 
   * @param args
   *          from command line
   */
  public static void main(String[] args) {
    CollectionValuedMap originalMap = new CollectionValuedMap<>();
    /*
        for (int i=0; i<4; i++) {
          for (int j=0; j<4; j++) {
            originalMap.add(new Integer(i), new Integer(j));
          }
        }
        originalMap.remove(new Integer(2));
        System.out.println("Map: ");
        System.out.println(originalMap);
        System.exit(0);
    */
    Random r = new Random();
    for (int i = 0; i < 800; i++) {
      Integer rInt1 = Integer.valueOf(r.nextInt(400));
      Integer rInt2 = Integer.valueOf(r.nextInt(400));
      originalMap.add(rInt1, rInt2);
      System.out.println("Adding " + rInt1 + ' ' + rInt2);
    }
    CollectionValuedMap originalCopyMap = new CollectionValuedMap<>(originalMap);
    CollectionValuedMap deltaCopyMap = new CollectionValuedMap<>(originalMap);
    CollectionValuedMap deltaMap = new DeltaCollectionValuedMap<>(originalMap);
    // now make a lot of changes to deltaMap;
    // add and change some stuff
    for (int i = 0; i < 400; i++) {
      Integer rInt1 = Integer.valueOf(r.nextInt(400));
      Integer rInt2 = Integer.valueOf(r.nextInt(400) + 1000);
      deltaMap.add(rInt1, rInt2);
      deltaCopyMap.add(rInt1, rInt2);
      System.out.println("Adding " + rInt1 + ' ' + rInt2);
    }
    // remove some stuff
    for (int i = 0; i < 400; i++) {
      Integer rInt1 = Integer.valueOf(r.nextInt(1400));
      Integer rInt2 = Integer.valueOf(r.nextInt(1400));
      deltaMap.removeMapping(rInt1, rInt2);
      deltaCopyMap.removeMapping(rInt1, rInt2);
      System.out.println("Removing " + rInt1 + ' ' + rInt2);
    }
    System.out.println("original: " + originalMap);
    System.out.println("copy: " + deltaCopyMap);
    System.out.println("delta: " + deltaMap);

    System.out.println("Original preserved? " + originalCopyMap.equals(originalMap));
    System.out.println("Delta accurate? " + deltaMap.equals(deltaCopyMap));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy