2 * Copyright (c) 2018 ZTE Corp. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.dom.broker;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.collect.HashBasedTable;
15 import com.google.common.collect.HashMultimap;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.ImmutableMap.Builder;
19 import com.google.common.collect.ImmutableTable;
20 import com.google.common.collect.ListMultimap;
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Maps;
23 import com.google.common.collect.Multimaps;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.EventListener;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Map.Entry;
32 import org.eclipse.jdt.annotation.NonNull;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
37 * Abstract routing table definition for Action and RPC.
39 * @param <I> instance type of RPC or Acton
40 * @param <D> identifier type of RPC or Acton
41 * @param <M> implementation type of RPC or Acton
42 * @param <L> listener type of RPC or Acton
43 * @param <E> routing entry type of RPC or Acton
44 * @param <K> routing key type
47 abstract class AbstractDOMRoutingTable<I, D, M, L extends EventListener, K,
48 E extends AbstractDOMRoutingTableEntry<D, M, L, K>> {
49 private final Map<K, E> operations;
50 private final EffectiveModelContext schemaContext;
52 AbstractDOMRoutingTable(final Map<K, E> operations, final EffectiveModelContext schemaContext) {
53 this.operations = requireNonNull(operations);
54 this.schemaContext = schemaContext;
57 AbstractDOMRoutingTable<I, D, M, L, K, E> setSchemaContext(final EffectiveModelContext context) {
58 final Builder<K, E> b = ImmutableMap.builder();
60 for (Entry<K, E> e : operations.entrySet()) {
61 final E entry = createOperationEntry(context, e.getKey(), e.getValue().getImplementations());
63 b.put(e.getKey(), entry);
67 return newInstance(b.build(), context);
70 AbstractDOMRoutingTable<I, D, M, L, K, E> add(final M implementation, final Set<I> oprsToAdd) {
71 if (oprsToAdd.isEmpty()) {
75 // First decompose the identifiers to a multimap
76 final ListMultimap<K, D> toAdd = decomposeIdentifiers(oprsToAdd);
78 final Builder<K, E> mb = ImmutableMap.builder();
80 // Now iterate over existing entries, modifying them as appropriate...
81 for (Entry<K, E> re : this.operations.entrySet()) {
82 List<D> newOperations = new ArrayList<>(toAdd.removeAll(re.getKey()));
83 if (!newOperations.isEmpty()) {
84 final E ne = (E) re.getValue().add(implementation, newOperations);
85 mb.put(re.getKey(), ne);
91 // Finally add whatever is left in the decomposed multimap
92 for (Entry<K, Collection<D>> e : toAdd.asMap().entrySet()) {
93 final Builder<D, List<M>> vb = ImmutableMap.builder();
94 final List<M> v = ImmutableList.of(implementation);
95 for (D i : e.getValue()) {
99 final E entry = createOperationEntry(schemaContext, e.getKey(), vb.build());
101 mb.put(e.getKey(), entry);
105 return newInstance(mb.build(), schemaContext);
108 AbstractDOMRoutingTable<I, D, M, L, K, E> addAll(final ImmutableTable<K, D, M> impls) {
109 if (impls.isEmpty()) {
113 // Create a temporary map, which we will mutatate
114 final var toAdd = HashBasedTable.create(impls);
115 final var mb = ImmutableMap.<K, E>builder();
117 // Now iterate over existing entries, modifying them as appropriate...
118 for (Entry<K, E> re : this.operations.entrySet()) {
119 final var newImpls = toAdd.rowMap().remove(re.getKey());
120 if (newImpls == null) {
126 for (var oper : newImpls.entrySet()) {
127 @SuppressWarnings("unchecked")
128 final E newVal = (E) ne.getValue().add(oper.getValue(), Lists.newArrayList(oper.getKey()));
129 ne = Map.entry(ne.getKey(), newVal);
134 // Finally add whatever is left in the decomposed multimap
135 for (Entry<K, Map<D, M>> e : toAdd.rowMap().entrySet()) {
136 final E entry = createOperationEntry(schemaContext, e.getKey(), ImmutableMap.copyOf(
137 Maps.<D, M, List<M>>transformValues(e.getValue(), ImmutableList::of)));
139 mb.put(e.getKey(), entry);
143 return newInstance(mb.build(), schemaContext);
146 AbstractDOMRoutingTable<I, D, M, L, K, E> remove(final M implementation, final Set<I> instances) {
147 if (instances.isEmpty()) {
151 // First decompose the identifiers to a multimap
152 final ListMultimap<K, D> toRemove = decomposeIdentifiers(instances);
154 // Now iterate over existing entries, modifying them as appropriate...
155 final Builder<K, E> b = ImmutableMap.builder();
156 for (Entry<K, E> e : this.operations.entrySet()) {
157 final List<D> removed = new ArrayList<>(toRemove.removeAll(e.getKey()));
158 if (!removed.isEmpty()) {
159 final E ne = (E) e.getValue().remove(implementation, removed);
161 b.put(e.getKey(), ne);
168 // All done, whatever is in toRemove, was not there in the first place
169 return newInstance(b.build(), schemaContext);
172 AbstractDOMRoutingTable<I, D, M, L, K, E> removeAll(final ImmutableTable<K, D, M> impls) {
173 if (impls.isEmpty()) {
177 // Create a temporary map, which we will mutatate
178 final var toRemove = HashBasedTable.create(impls);
179 final var mb = ImmutableMap.<K, E>builder();
181 // Now iterate over existing entries, modifying them as appropriate...
182 for (Entry<K, E> re : this.operations.entrySet()) {
183 final var oldImpls = toRemove.rowMap().remove(re.getKey());
184 if (oldImpls == null) {
190 for (var oper : oldImpls.entrySet()) {
192 @SuppressWarnings("unchecked")
193 final E newVal = (E) ne.getValue().remove(oper.getValue(), Lists.newArrayList(oper.getKey()));
194 if (newVal != null) {
195 ne = Map.entry(ne.getKey(), newVal);
206 // All done, whatever is in toRemove, was not there in the first place
207 return newInstance(mb.build(), schemaContext);
210 static final <K, V> HashMultimap<V, K> invertImplementationsMap(final Map<K, V> map) {
211 return Multimaps.invertFrom(Multimaps.forMap(map), HashMultimap.create());
215 Map<K, Set<D>> getOperations() {
216 return Maps.transformValues(operations, AbstractDOMRoutingTableEntry::registeredIdentifiers);
219 Map<K, Set<D>> getOperations(final L listener) {
220 final Map<K, Set<D>> ret = new HashMap<>(operations.size());
221 for (Entry<K, E> e : operations.entrySet()) {
222 final Set<D> ids = e.getValue().registeredIdentifiers(listener);
223 if (!ids.isEmpty()) {
224 ret.put(e.getKey(), ids);
231 @Nullable AbstractDOMRoutingTableEntry<D, M, L, K> getEntry(final @NonNull K type) {
232 return operations.get(type);
235 protected abstract AbstractDOMRoutingTable<I, D, M, L, K, E> newInstance(Map<K, E> operations,
236 EffectiveModelContext schemaContext);
238 abstract ListMultimap<K, D> decomposeIdentifiers(Set<I> instances);
240 abstract @Nullable E createOperationEntry(EffectiveModelContext context, K key, Map<D, List<M>> implementations);