001/*
002 * JPPF.
003 * Copyright (C) 2005-2016 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.utils.collections;
020
021import java.util.*;
022import java.util.Map.Entry;
023import java.util.concurrent.locks.Lock;
024
025/**
026 * A map whose values are collections of a given component type.
027 * @param <K> the type of keys in the map.
028 * @param <V> the type of values in the collections mapped to the keys.
029 * @author Laurent Cohen
030 */
031public abstract class AbstractCollectionMap<K, V> implements CollectionMap<K, V> {
032  /**
033   * The underlying map to which operations are delegated.
034   */
035  protected Map<K, Collection<V>> map = null;
036
037  /**
038   * Default constructor.
039   */
040  public AbstractCollectionMap() {
041  }
042
043  @Override
044  public void putValue(final K key, final V value) {
045    Collection<V> coll = createOrGetCollection(key);
046    coll.add(value);
047  }
048
049  @Override
050  public boolean removeValue(final K key, final V value) {
051    Collection<V> coll = map.get(key);
052    if (coll != null) {
053      boolean b = coll.remove(value);
054      if (coll.isEmpty()) map.remove(key);
055      return b;
056    }
057    return false;
058  }
059
060  @Override
061  public void addValues(final K key, final Collection<V> values) {
062    Collection<V> coll = createOrGetCollection(key);
063    coll.addAll(values);
064  }
065
066  @Override
067  public void addValues(final K key, final V...values) {
068    Collection<V> coll = createOrGetCollection(key);
069    for (V value: values) coll.add(value);
070  }
071
072  @Override
073  public int removeValues(final K key, final V...values) {
074    Collection<V> coll = map.get(key);
075    if (coll != null) {
076      int count = 0;
077      for (V value: values) {
078        if (coll.remove(value)) count++;
079      }
080      if (coll.isEmpty()) map.remove(key);
081      return count;
082    }
083    return 0;
084  }
085
086  @Override
087  public Collection<V> removeKey(final K key) {
088    return map.remove(key);
089  }
090
091  @Override
092  public Collection<V> getValues(final K key) {
093    return map.get(key);
094  }
095
096  @Override
097  public int size() {
098    int result = 0;
099    for (Map.Entry<K, Collection<V>> entry: map.entrySet()) result += entry.getValue().size();
100    return result;
101  }
102
103  @Override
104  public boolean isEmpty() {
105    return map.isEmpty();
106  }
107
108  @Override
109  public boolean containsKey(final K key) {
110    return map.containsKey(key);
111  }
112
113  @Override
114  public boolean containsValue(final K key, final V value) {
115    Collection<V> coll = map.get(key);
116    if (coll == null) return false;
117    return coll.contains(value);
118  }
119
120  @Override
121  public boolean containsValue(final V value) {
122    for (Map.Entry<K, Collection<V>> entry: map.entrySet()) {
123      if (entry.getValue().contains(value)) return true;
124    }
125    return false;
126  }
127
128  @Override
129  public Iterator<V> iterator() {
130    return new CollectionMapIterator();
131  }
132
133  @Override
134  public Iterator<V> iterator(final Lock lock) {
135    return new CollectionMapIterator(lock);
136  }
137
138  @Override
139  public void clear() {
140    map.clear();
141  }
142
143  /**
144   * Create a new  map.
145   * @return a new mutable empty map.
146   */
147  protected abstract Map<K, Collection<V>> createMap();
148
149  /**
150   * Create a new collection of values for insertion into the map.
151   * @return a new mutable empty collection.
152   */
153  protected abstract Collection<V> newCollection();
154
155  @Override
156  public String toString() {
157    return map.toString();
158  }
159
160  /**
161   * An iterator on the values in the mapped collections of this map.
162   */
163  private class CollectionMapIterator implements Iterator<V> {
164    /**
165     * Iterator over the entries in the priority map.
166     */
167    private Iterator<Map.Entry<K, Collection<V>>> entryIterator = null;
168    /**
169     * Iterator over the task bundles in the map entry specified by <code>entryIterator</code>.
170     */
171    private Iterator<V> listIterator = null;
172    /**
173     * Used for synchronized access to the queue.
174     */
175    private final Lock lock;
176
177    /**
178     * Initialize this iterator.
179     */
180    public CollectionMapIterator() {
181      this(null);
182    }
183
184    /**
185     * Initialize this iterator.
186     * @param lock used to synchronize with the queue.
187     */
188    public CollectionMapIterator(final Lock lock) {
189      this.lock = lock;
190      lock();
191      try {
192        entryIterator = map.entrySet().iterator();
193        if (entryIterator.hasNext()) listIterator = entryIterator.next().getValue().iterator();
194      } finally {
195        unlock();
196      }
197    }
198
199    /**
200     * Determines whether an element remains to visit.
201     * @return true if there is at least one element that hasn't been visited, false otherwise.
202     */
203    @Override
204    public boolean hasNext() {
205      lock();
206      try {
207        return entryIterator.hasNext() || ((listIterator != null) && listIterator.hasNext());
208      } finally {
209        unlock();
210      }
211    }
212
213    /**
214     * Get the next element for this iterator.
215     * @return the next element as a <code>JPPFTaskBundle</code> instance.
216     */
217    @Override
218    public V next() {
219      lock();
220      try {
221        if (listIterator != null) {
222          if (listIterator.hasNext()) return listIterator.next();
223          if (entryIterator.hasNext()) {
224            listIterator = entryIterator.next().getValue().iterator();
225            if (listIterator.hasNext()) return listIterator.next();
226          }
227        }
228        throw new NoSuchElementException("no more element for this iterator");
229      } finally {
230        unlock();
231      }
232    }
233
234    /**
235     * This operation is not supported and throws an <code>UnsupportedOperationException</code>.
236     * @throws UnsupportedOperationException as this operation is not supported.
237     */
238    @Override
239    public void remove() throws UnsupportedOperationException {
240      throw new UnsupportedOperationException("remove() is not supported on a CollectionMapIterator");
241    }
242
243    /**
244     * Perform a lock if a lock is present.
245     */
246    private void lock() {
247      if (lock != null) lock.lock();
248    }
249
250    /**
251     * Perform an unlock if a lock is present.
252     */
253    private void unlock() {
254      if (lock != null) lock.unlock();
255    }
256  }
257
258  @Override
259  public Set<K> keySet() {
260    return map == null ? null : map.keySet();
261  }
262
263  @Override
264  public Set<Entry<K, Collection<V>>> entrySet() {
265    return map == null ? null : map.entrySet();
266  }
267
268  @Override
269  public List<V> allValues() {
270    List<V> list = new ArrayList<>();
271    for (Map.Entry<K, Collection<V>> entry: map.entrySet()) {
272      if (!entry.getValue().isEmpty()) list.addAll(entry.getValue());
273    }
274    return list;
275  }
276
277  /**
278   * Get an exisitng collection for the specified key, or create it if it doesn't exist.
279   * @param key the key for which to get a collection of values.
280   * @return a collection of value for the specified keys, may be empty if newly created.
281   */
282  protected Collection<V> createOrGetCollectionSynchronized(final K key) {
283    Collection<V> coll;
284    synchronized(map) {
285      coll = map.get(key);
286      if (coll == null) {
287        coll = newCollection();
288        map.put(key, coll);
289      }
290    }
291    return coll;
292  }
293
294  /**
295   * Get an exisitng collection for the specified key, or create it if it doesn't exist.
296   * @param key the key for which to get a collection of values.
297   * @return a collection of value for the specified keys, may be empty if newly created.
298   */
299  protected Collection<V> createOrGetCollection(final K key) {
300    Collection<V> coll;
301    synchronized(map) {
302      coll = map.get(key);
303      if (coll == null) {
304        coll = newCollection();
305        map.put(key, coll);
306      }
307    }
308    return coll;
309  }
310}