001/*
002 * Copyright c 2018 Rusi Popov, MDA Tools.net All rights reserved.
003 *
004 * This program and the accompanying materials are made available under the terms of the
005 * Eclipse Public License v2.0 which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v20.html
007 */
008package net.mdatools.modelant.core.util.map;
009
010import java.io.Serializable;
011import java.util.Collection;
012import java.util.Collections;
013import java.util.HashMap;
014import java.util.Map;
015import java.util.Set;
016
017/**
018 * Map each key to a collection of values
019 * @param <K> the class of the key
020 * @param <V> the class of the elements held in the collection
021 */
022public abstract class MapToCollection<K, V> implements Serializable {
023
024  /**
025   * The internal map representation.
026   * INVARIANT:
027   *  it maps keys to Collections of values
028   */
029  private final Map<K, Collection<V>> map = new HashMap<K, Collection<V>>();
030
031  /**
032   * @see java.util.Map#clear()
033   */
034  public final void clear() {
035    map.clear();
036  }
037
038
039  /**
040   * @see java.util.Map#containsKey(java.lang.Object)
041   */
042  public final boolean containsKey(K key) {
043    return map.containsKey( key );
044  }
045
046
047  /**
048   * @param key
049   * @return null or a a non-empty list of values
050   * @see java.util.Map#get(java.lang.Object)
051   */
052  public Collection<V> get(K key) {
053    Collection<V> result;
054
055    result = map.get( key );
056
057    return result;
058  }
059
060
061  /**
062   * @see java.util.Map#isEmpty()
063   */
064  public final boolean isEmpty() {
065    return map.isEmpty();
066  }
067
068
069  /**
070   * @see java.util.Map#keySet()
071   */
072  public final Set<K> keySet() {
073    return map.keySet();
074  }
075
076  /**
077   * @see java.util.Map#keySet()
078   */
079  public final Set<Map.Entry<K, Collection<V>>> entrySet() {
080    return map.entrySet();
081  }
082
083
084
085  /**
086   * @return the list of all actual mappings bound to this key.
087   *         NOTE: this is different than the the inherited specification, where
088   *               the previous value bound to the key is required to be returned
089   * @see java.util.Map#put(java.lang.Object, java.lang.Object)
090   */
091  public final Collection<V> put(K key, V value) {
092    Collection<V> result = get( key );
093
094    if ( result == null ) {
095      result = createValuesCollection();
096      map.put( key, result );
097    }
098    result.add( value );
099    return result;
100  }
101
102
103  public final Collection<V> put(K key, Collection<V> value) {
104    Collection<V> result = get(key);
105
106    if (result == null) {
107      result = createValuesCollection();
108      result.addAll(value);
109      map.put(key, result);
110    }
111    return result;
112  }
113
114
115  /**
116   * @see java.util.Map#putAll(java.util.Map)
117   */
118  public final void putAll(MapToCollection<K, V> t) {
119    for (Map.Entry<K, Collection<V>> entry : t.entrySet()) {
120      put( entry.getKey(), entry.getValue() );
121    }
122  }
123
124
125  /**
126   * Removes the whole collection bound to the key
127   * @return the original values collection bound to that key
128   * @see java.util.Map#remove(java.lang.Object)
129   */
130  public final Collection<V> remove(K key) {
131    return map.remove( key );
132  }
133
134  /**
135   * Removes the value from the collection for that key
136   * @return true if actually removed the value
137   * @see java.util.Map#remove(java.lang.Object)
138   */
139  public final boolean remove(K key, V value) {
140    boolean result;
141    Collection<V> values;
142
143    values = get( key );
144    if ( values != null ) {
145      result = values.remove( value );
146      if ( values.isEmpty() ) {
147        map.remove( key );
148      }
149    } else {
150      result = false;
151    }
152    return result;
153  }
154
155  /**
156   * @return a collection of all values in this multi-value map
157   * @see java.util.Map#values()
158   */
159  public final Collection<V> values() {
160    Collection<V> result;
161    Collection<V> values;
162
163    result = createValuesCollection();
164
165    for(Map.Entry<K, Collection<V>> entry : map.entrySet() ) {
166      values = entry.getValue();
167
168      if ( values != null ) {
169        result.addAll( values );
170      }
171    }
172    return result;
173  }
174
175
176  /**
177   * This is a factory method for nested collections
178   */
179  protected abstract Collection<V> createValuesCollection();
180
181
182  /**
183   * @see java.lang.Object#toString()
184   */
185  public String toString() {
186    StringBuilder result = new StringBuilder( 2048 );
187
188    result//.append( getClass().getSimpleName() )
189          .append( " {" );
190
191    for(Map.Entry<K, Collection<V>> entry : map.entrySet() ) {
192      result.append( "\r\n" )
193            .append( entry.getKey() )
194            .append( " = ")
195            .append( entry.getValue() );
196    }
197    if (!map.isEmpty()) {
198      result.append( "\r\n" );
199    }
200    result.append("}");
201    return result.toString();
202  }
203
204  /**
205   * @return convert this to a read-only map to collections
206   */
207  public final Map<K, Collection<V>> toMap() {
208    return Collections.unmodifiableMap( map );
209  }
210}