Setting stateful02 to be primary draft version.
[bgpcep.git] / concepts / src / main / java / org / opendaylight / protocol / concepts / MultiRegistry.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.concepts;
9
10 import java.util.List;
11 import java.util.concurrent.ConcurrentHashMap;
12 import java.util.concurrent.ConcurrentMap;
13
14 import javax.annotation.concurrent.GuardedBy;
15 import javax.annotation.concurrent.ThreadSafe;
16
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 import com.google.common.collect.ArrayListMultimap;
21 import com.google.common.collect.ListMultimap;
22
23 /**
24  * A registry which allows multiple values for a particular key. One of those
25  * is considered the best and returned as the representative.
26  * 
27  * When selecting the candidate, we evaluate the order of insertion, picking the
28  * value inserted first, but then we look at all the other candidates and if there
29  * is one which is a subclass of the first one, we select that one.
30  *
31  * @param <K> key type
32  * @param <V> value type
33  */
34 @ThreadSafe
35 public final class MultiRegistry<K, V> {
36         private static final Logger LOG = LoggerFactory.getLogger(MultiRegistry.class);
37         private final ConcurrentMap<K, V> current = new ConcurrentHashMap<>();
38
39         @GuardedBy("this")
40         private final ListMultimap<K, V> candidates = ArrayListMultimap.create();
41
42         @GuardedBy("this")
43         private void updateCurrent(final K key) {
44                 final List<V> values = candidates.get(key);
45
46                 // Simple case: no candidates
47                 if (values.isEmpty()) {
48                         current.remove(key);
49                         return;
50                 }
51
52                 V best = values.get(0);
53                 for (V v : values) {
54                         final Class<?> vc = v.getClass();
55                         final Class<?> bc = best.getClass();
56                         if (bc.isAssignableFrom(vc)) {
57                                 LOG.debug("{} is superclass of {}, preferring the latter", bc, vc);
58                                 best = v;
59                         } else if (vc.isAssignableFrom(bc)) {
60                                 LOG.debug("{} is subclass of {}, preferring the former", bc, vc);
61                         } else {
62                                 LOG.debug("{} and {} are not related, keeping the former", bc, vc);
63                         }
64                 }
65
66                 LOG.debug("New best value {}", best);
67                 current.put(key, best);
68         }
69
70         public synchronized AbstractRegistration register(final K key, final V value) {
71                 candidates.put(key, value);
72                 updateCurrent(key);
73
74                 final Object lock = this;
75                 return new AbstractRegistration() {
76                         @Override
77                         protected void removeRegistration() {
78                                 synchronized (lock) {
79                                         candidates.remove(key, value);
80                                         updateCurrent(key);
81                                 }
82                         }
83                 };
84         }
85
86         public V get(final K key) {
87                 return this.current.get(key);
88         }
89 }