    001/*
    002 * Copyright (c) 2007-2017 Xplenty, Inc. All Rights Reserved.
    003 *
    004 * Project and contact information: http://www.cascading.org/
    005 *
    006 * This file is part of the Cascading project.
    007 *
    008 * Licensed under the Apache License, Version 2.0 (the "License");
    009 * you may not use this file except in compliance with the License.
    010 * You may obtain a copy of the License at
    011 *
    012 *     http://www.apache.org/licenses/LICENSE-2.0
    013 *
    014 * Unless required by applicable law or agreed to in writing, software
    015 * distributed under the License is distributed on an "AS IS" BASIS,
    016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    017 * See the License for the specific language governing permissions and
    018 * limitations under the License.
    019 */
    020
    021package cascading.util;
    022
    023import java.io.Serializable;
    024import java.util.Arrays;
    025import java.util.Collection;
    026import java.util.HashSet;
    027import java.util.Map;
    028import java.util.Set;
    029
    030/**
    031 *
    032 */
    033public abstract class MultiMap<C extends Collection<V>, K, V> implements Serializable
    034  {
    035  private Map<K, C> map = null;
    036
    037  protected Map<K, C> getMap()
    038    {
    039    if( map == null )
    040      map = createMap();
    041
    042    return map;
    043    }
    044
    045  protected abstract Map<K, C> createMap();
    046
    047  protected abstract C createCollection();
    048
    049  protected abstract C emptyCollection();
    050
    051  public boolean containsKey( K key )
    052    {
    053    return getMap().containsKey( key );
    054    }
    055
    056  public Set<K> getKeys()
    057    {
    058    return getMap().keySet();
    059    }
    060
    061  public Set<Map.Entry<K, C>> getEntries()
    062    {
    063    return getMap().entrySet();
    064    }
    065
    066  public void addAll( MultiMap<C, K, V> sourceMap )
    067    {
    068    if( sourceMap == null )
    069      return;
    070
    071    Map<K, C> destinationMap = sourceMap.getMap();
    072
    073    for( Map.Entry<K, C> entry : destinationMap.entrySet() )
    074      addAll( entry.getKey(), entry.getValue() );
    075    }
    076
    077  public void put( K key, V value )
    078    {
    079    getMultiValues( key ).add( value );
    080    }
    081
    082  public C remove( K key )
    083    {
    084    return getMap().remove( key );
    085    }
    086
    087  protected C getMultiValues( K key )
    088    {
    089    Map<K, C> map = getMap();
    090    C values = map.get( key );
    091
    092    if( values == null )
    093      {
    094      values = createCollection();
    095      map.put( key, values );
    096      }
    097
    098    return values;
    099    }
    100
    101  public void addAll( K key, V... values )
    102    {
    103    addAll( key, Arrays.asList( values ) );
    104    }
    105
    106  public void addAll( K key, Collection<V> values )
    107    {
    108    getMultiValues( key ).addAll( values );
    109    }
    110
    111  public C getValues()
    112    {
    113    if( getMap().isEmpty() )
    114      return emptyCollection();
    115
    116    C results = createCollection();
    117
    118    for( C values : getMap().values() )
    119      results.addAll( values );
    120
    121    return results;
    122    }
    123
    124  public C getValues( K key )
    125    {
    126    C values = getMap().get( key );
    127
    128    if( values == null )
    129      return emptyCollection();
    130
    131    return values;
    132    }
    133
    134  public C getAllValues( K... keys )
    135    {
    136    if( keys.length == 0 )
    137      return emptyCollection();
    138
    139    if( keys.length == 1 )
    140      return getValues( keys[ 0 ] );
    141
    142    C values = createCollection();
    143
    144    for( K key : keys )
    145      {
    146      C current = getMap().get( key );
    147
    148      if( current != null )
    149        values.addAll( current );
    150      }
    151
    152    return values;
    153    }
    154
    155  public Set<K> getKeysFor( V value )
    156    {
    157    Set<K> results = new HashSet<>();
    158
    159    for( Map.Entry<K, C> entry : getMap().entrySet() )
    160      {
    161      if( entry.getValue().contains( value ) )
    162        results.add( entry.getKey() );
    163      }
    164
    165    return results;
    166    }
    167
    168  public boolean hasKey( V value )
    169    {
    170    Set<K> keys = getKeysFor( value );
    171
    172    return keys != null && !keys.isEmpty();
    173    }
    174
    175  public boolean hadKey( K key, V value )
    176    {
    177    C values = getMap().get( key );
    178
    179    return values != null && values.contains( value );
    180    }
    181
    182  public boolean isEmpty()
    183    {
    184    return getMap().isEmpty();
    185    }
    186
    187  @Override
    188  public String toString()
    189    {
    190    final StringBuilder sb = new StringBuilder( "MultiMap{" );
    191    sb.append( "map=" ).append( getMap() );
    192    sb.append( '}' );
    193    return sb.toString();
    194    }
    195
    196  @Override
    197  public boolean equals( Object object )
    198    {
    199    if( this == object )
    200      return true;
    201
    202    if( !( object instanceof MultiMap ) )
    203      return false;
    204
    205    MultiMap multiMap = (MultiMap) object;
    206
    207    if( map != null ? !map.equals( multiMap.map ) : multiMap.map != null )
    208      return false;
    209
    210    return true;
    211    }
    212
    213  @Override
    214  public int hashCode()
    215    {
    216    return map != null ? map.hashCode() : 0;
    217    }
    218  }