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 }