Add DOMRpcProviderService bulk registration
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / AbstractDOMRoutingTable.java
1 /*
2  * Copyright (c) 2018 ZTE Corp. 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.mdsal.dom.broker;
9
10 import static java.util.Objects.requireNonNull;
11
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;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Set;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32
33 /**
34  * Abstract routing table definition for Action and RPC.
35  *
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
42  */
43 @Beta
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;
48
49     AbstractDOMRoutingTable(final Map<K, E> operations, final EffectiveModelContext schemaContext) {
50         this.operations = requireNonNull(operations);
51         this.schemaContext = schemaContext;
52     }
53
54     AbstractDOMRoutingTable<I, D, M, L, K, E> setSchemaContext(final EffectiveModelContext context) {
55         final Builder<K, E> b = ImmutableMap.builder();
56
57         for (Entry<K, E> e : operations.entrySet()) {
58             final E entry = createOperationEntry(context, e.getKey(), e.getValue().getImplementations());
59             if (entry != null) {
60                 b.put(e.getKey(), entry);
61             }
62         }
63
64         return newInstance(b.build(), context);
65     }
66
67     AbstractDOMRoutingTable<I, D, M, L, K, E> add(final M implementation, final Set<I> oprsToAdd) {
68         if (oprsToAdd.isEmpty()) {
69             return this;
70         }
71
72         final Builder<K, E> mb = ImmutableMap.builder();
73         add(implementation, oprsToAdd, mb);
74
75         return newInstance(mb.build(), schemaContext);
76     }
77
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);
82
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);
89             } else {
90                 tableInputBuilder.put(re);
91             }
92         }
93
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()) {
99                 vb.put(i, v);
100             }
101
102             final E entry = createOperationEntry(schemaContext, e.getKey(), vb.build());
103             if (entry != null) {
104                 tableInputBuilder.put(e.getKey(), entry);
105             }
106         }
107     }
108
109     AbstractDOMRoutingTable<I, D, M, L, K, E> addAll(final Map<I, M> map) {
110         if (map.isEmpty()) {
111             return this;
112         }
113
114         HashMultimap<M, I> inverted = invertImplementationsMap(map);
115
116         final Builder<K, E> tableInputBuilder = ImmutableMap.builder();
117         for (M impl : inverted.keySet()) {
118             add(impl, inverted.get(impl), tableInputBuilder);
119         }
120
121         return newInstance(tableInputBuilder.build(), schemaContext);
122     }
123
124     AbstractDOMRoutingTable<I, D, M, L, K, E> remove(final M implementation, final Set<I> instances) {
125         if (instances.isEmpty()) {
126             return this;
127         }
128
129         // First decompose the identifiers to a multimap
130         final ListMultimap<K, D> toRemove = decomposeIdentifiers(instances);
131
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);
138                 if (ne != null) {
139                     b.put(e.getKey(), ne);
140                 }
141             } else {
142                 b.put(e);
143             }
144         }
145
146         // All done, whatever is in toRemove, was not there in the first place
147         return newInstance(b.build(), schemaContext);
148     }
149
150
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);
154
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);
159                 if (ne != null) {
160                     tableInputBuilder.put(e.getKey(), ne);
161                 }
162             } else {
163                 tableInputBuilder.put(e);
164             }
165         }
166     }
167
168     AbstractDOMRoutingTable<I, D, M, L, K, E> removeAll(final Map<I, M> map) {
169         if (map.isEmpty()) {
170             return this;
171         }
172         HashMultimap<M, I> inverted = invertImplementationsMap(map);
173
174         final Builder<K, E> tableInputBuilder = ImmutableMap.builder();
175         for (M impl : inverted.keySet()) {
176             remove(impl, inverted.get(impl), tableInputBuilder);
177         }
178
179         return newInstance(tableInputBuilder.build(), schemaContext);
180     }
181
182     HashMultimap<M, I> invertImplementationsMap(final Map<I, M> map) {
183         return Multimaps.invertFrom(Multimaps.forMap(map), HashMultimap.create());
184     }
185
186     @VisibleForTesting
187     Map<K, Set<D>> getOperations() {
188         return Maps.transformValues(operations, AbstractDOMRoutingTableEntry::registeredIdentifiers);
189     }
190
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);
197             }
198         }
199
200         return ret;
201     }
202
203     @Nullable AbstractDOMRoutingTableEntry<D, M, L, K> getEntry(final @NonNull K type) {
204         return operations.get(type);
205     }
206
207     protected abstract AbstractDOMRoutingTable<I, D, M, L, K, E> newInstance(Map<K, E> operations,
208             EffectiveModelContext schemaContext);
209
210     abstract ListMultimap<K, D> decomposeIdentifiers(Set<I> instances);
211
212     abstract E createOperationEntry(EffectiveModelContext context, K key, Map<D, List<M>> implementations);
213 }