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}