/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.protocol.concepts; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.checkerframework.checker.lock.qual.GuardedBy; import org.checkerframework.checker.lock.qual.Holding; import org.opendaylight.yangtools.concepts.AbstractRegistration; import org.opendaylight.yangtools.concepts.Registration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A registry which allows multiple values for a particular key. One of those is considered the best and returned as the * representative. * When selecting the candidate, we evaluate the order of insertion, picking the value inserted first, but then we look * at all the other candidates and if there is one which is a subclass of the first one, we select that one. * *

* This class is thread-safe. * * @param key type * @param value type */ public final class MultiRegistry { private static final Logger LOG = LoggerFactory.getLogger(MultiRegistry.class); private final ConcurrentMap current = new ConcurrentHashMap<>(); @GuardedBy("this") private final ListMultimap candidates = ArrayListMultimap.create(); @Holding("this") private void updateCurrent(final K key) { final List values = this.candidates.get(key); // Simple case: no candidates if (values.isEmpty()) { this.current.remove(key); return; } V best = values.get(0); for (V v : values) { final Class vc = v.getClass(); final Class bc = best.getClass(); if (bc.isAssignableFrom(vc)) { LOG.debug("{} is superclass of {}, preferring the latter", bc, vc); best = v; } else if (vc.isAssignableFrom(bc)) { LOG.debug("{} is subclass of {}, preferring the former", bc, vc); } else { LOG.debug("{} and {} are not related, keeping the former", bc, vc); } } LOG.debug("New best value {}", best); this.current.put(key, best); } public synchronized Registration register(final K key, final V value) { this.candidates.put(key, value); updateCurrent(key); return new AbstractRegistration() { @Override protected void removeRegistration() { synchronized (MultiRegistry.this) { MultiRegistry.this.candidates.remove(key, value); updateCurrent(key); } } }; } public V get(final K key) { return this.current.get(key); } public Iterable getAllValues() { return Iterables.unmodifiableIterable(this.current.values()); } }