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.HashMultimap;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableMap.Builder;
18 import com.google.common.collect.ListMultimap;
19 import com.google.common.collect.Maps;
20 import com.google.common.collect.Multimaps;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.EventListener;
24 import java.util.HashMap;
25 import java.util.List;
27 import java.util.Map.Entry;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
34 * Abstract routing table definition for Action and RPC.
36 * @param <I> instance type of RPC or Acton
37 * @param <D> identifier type of RPC or Acton
38 * @param <M> implementation type of RPC or Acton
39 * @param <L> listener type of RPC or Acton
40 * @param <E> routing entry type of RPC or Acton
41 * @param <K> routing key type
44 abstract class AbstractDOMRoutingTable<I, D, M, L extends EventListener, K,
45 E extends AbstractDOMRoutingTableEntry<D, M, L, K>> {
46 private final Map<K, E> operations;
47 private final EffectiveModelContext schemaContext;
49 AbstractDOMRoutingTable(final Map<K, E> operations, final EffectiveModelContext schemaContext) {
50 this.operations = requireNonNull(operations);
51 this.schemaContext = schemaContext;
54 AbstractDOMRoutingTable<I, D, M, L, K, E> setSchemaContext(final EffectiveModelContext context) {
55 final Builder<K, E> b = ImmutableMap.builder();
57 for (Entry<K, E> e : operations.entrySet()) {
58 final E entry = createOperationEntry(context, e.getKey(), e.getValue().getImplementations());
60 b.put(e.getKey(), entry);
64 return newInstance(b.build(), context);
67 AbstractDOMRoutingTable<I, D, M, L, K, E> add(final M implementation, final Set<I> oprsToAdd) {
68 if (oprsToAdd.isEmpty()) {
72 final Builder<K, E> mb = ImmutableMap.builder();
73 add(implementation, oprsToAdd, mb);
75 return newInstance(mb.build(), schemaContext);
78 private void add(final M implementation, final Set<I> oprsToAdd,
79 final Builder<K, E> tableInputBuilder) {
80 // First decompose the identifiers to a multimap
81 final ListMultimap<K, D> toAdd = decomposeIdentifiers(oprsToAdd);
83 // Now iterate over existing entries, modifying them as appropriate...
84 for (Entry<K, E> re : this.operations.entrySet()) {
85 List<D> newOperations = new ArrayList<>(toAdd.removeAll(re.getKey()));
86 if (!newOperations.isEmpty()) {
87 final E ne = (E) re.getValue().add(implementation, newOperations);
88 tableInputBuilder.put(re.getKey(), ne);
90 tableInputBuilder.put(re);
94 // Finally add whatever is left in the decomposed multimap
95 for (Entry<K, Collection<D>> e : toAdd.asMap().entrySet()) {
96 final Builder<D, List<M>> vb = ImmutableMap.builder();
97 final List<M> v = ImmutableList.of(implementation);
98 for (D i : e.getValue()) {
102 final E entry = createOperationEntry(schemaContext, e.getKey(), vb.build());
104 tableInputBuilder.put(e.getKey(), entry);
109 AbstractDOMRoutingTable<I, D, M, L, K, E> addAll(final Map<I, M> map) {
114 HashMultimap<M, I> inverted = invertImplementationsMap(map);
116 final Builder<K, E> tableInputBuilder = ImmutableMap.builder();
117 for (M impl : inverted.keySet()) {
118 add(impl, inverted.get(impl), tableInputBuilder);
121 return newInstance(tableInputBuilder.build(), schemaContext);
124 AbstractDOMRoutingTable<I, D, M, L, K, E> remove(final M implementation, final Set<I> instances) {
125 if (instances.isEmpty()) {
129 // First decompose the identifiers to a multimap
130 final ListMultimap<K, D> toRemove = decomposeIdentifiers(instances);
132 // Now iterate over existing entries, modifying them as appropriate...
133 final Builder<K, E> b = ImmutableMap.builder();
134 for (Entry<K, E> e : this.operations.entrySet()) {
135 final List<D> removed = new ArrayList<>(toRemove.removeAll(e.getKey()));
136 if (!removed.isEmpty()) {
137 final E ne = (E) e.getValue().remove(implementation, removed);
139 b.put(e.getKey(), ne);
146 // All done, whatever is in toRemove, was not there in the first place
147 return newInstance(b.build(), schemaContext);
151 private void remove(final M implementation, final Set<I> oprsToRemove,
152 final Builder<K, E> tableInputBuilder) {
153 final ListMultimap<K, D> toRemove = decomposeIdentifiers(oprsToRemove);
155 for (Entry<K, E> e : this.operations.entrySet()) {
156 final List<D> removed = new ArrayList<>(toRemove.removeAll(e.getKey()));
157 if (!removed.isEmpty()) {
158 final E ne = (E) e.getValue().remove(implementation, removed);
160 tableInputBuilder.put(e.getKey(), ne);
163 tableInputBuilder.put(e);
168 AbstractDOMRoutingTable<I, D, M, L, K, E> removeAll(final Map<I, M> map) {
172 HashMultimap<M, I> inverted = invertImplementationsMap(map);
174 final Builder<K, E> tableInputBuilder = ImmutableMap.builder();
175 for (M impl : inverted.keySet()) {
176 remove(impl, inverted.get(impl), tableInputBuilder);
179 return newInstance(tableInputBuilder.build(), schemaContext);
182 HashMultimap<M, I> invertImplementationsMap(final Map<I, M> map) {
183 return Multimaps.invertFrom(Multimaps.forMap(map), HashMultimap.create());
187 Map<K, Set<D>> getOperations() {
188 return Maps.transformValues(operations, AbstractDOMRoutingTableEntry::registeredIdentifiers);
191 Map<K, Set<D>> getOperations(final L listener) {
192 final Map<K, Set<D>> ret = new HashMap<>(operations.size());
193 for (Entry<K, E> e : operations.entrySet()) {
194 final Set<D> ids = e.getValue().registeredIdentifiers(listener);
195 if (!ids.isEmpty()) {
196 ret.put(e.getKey(), ids);
203 @Nullable AbstractDOMRoutingTableEntry<D, M, L, K> getEntry(final @NonNull K type) {
204 return operations.get(type);
207 protected abstract AbstractDOMRoutingTable<I, D, M, L, K, E> newInstance(Map<K, E> operations,
208 EffectiveModelContext schemaContext);
210 abstract ListMultimap<K, D> decomposeIdentifiers(Set<I> instances);
212 abstract E createOperationEntry(EffectiveModelContext context, K key, Map<D, List<M>> implementations);