Make DOMSchemaService operate of EffectiveModelContext
[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.ImmutableList;
15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.ImmutableMap.Builder;
17 import com.google.common.collect.ListMultimap;
18 import com.google.common.collect.Maps;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.EventListener;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Set;
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
30 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
31
32 /**
33  * Abstract routing table definition for Action and RPC.
34  * @param <I> instance type of RPC or Acton
35  * @param <D> identifier type of RPC or Acton
36  * @param <M> implementation type of RPC or Acton
37  * @param <L> listener type of RPC or Acton
38  * @param <E> routing entry type of RPC or Acton
39  */
40 @Beta
41 abstract class AbstractDOMRoutingTable<I, D, M, L extends EventListener,
42         E extends AbstractDOMRoutingTableEntry<D, M, L>> {
43     private final Map<SchemaPath, E> operations;
44     private final EffectiveModelContext schemaContext;
45
46     AbstractDOMRoutingTable(final Map<SchemaPath, E> operations, final EffectiveModelContext schemaContext) {
47         this.operations = requireNonNull(operations);
48         this.schemaContext = schemaContext;
49     }
50
51     AbstractDOMRoutingTable<I, D, M, L, E> setSchemaContext(final EffectiveModelContext context) {
52         final Builder<SchemaPath, E> b = ImmutableMap.builder();
53
54         for (Entry<SchemaPath, E> e : operations.entrySet()) {
55             final E entry = createOperationEntry(context, e.getKey(),
56                 e.getValue().getImplementations());
57             if (entry != null) {
58                 b.put(e.getKey(), entry);
59             }
60         }
61
62         return newInstance(b.build(), context);
63     }
64
65     AbstractDOMRoutingTable<I, D, M, L, E> add(final M implementation, final Set<I> oprsToAdd) {
66         if (oprsToAdd.isEmpty()) {
67             return this;
68         }
69
70         // First decompose the identifiers to a multimap
71         final ListMultimap<SchemaPath, D> toAdd = decomposeIdentifiers(oprsToAdd);
72
73         // Now iterate over existing entries, modifying them as appropriate...
74         final Builder<SchemaPath, E> mb = ImmutableMap.builder();
75         for (Entry<SchemaPath, E> re : this.operations.entrySet()) {
76             List<D> newOperations = new ArrayList<>(toAdd.removeAll(re.getKey()));
77             if (!newOperations.isEmpty()) {
78                 final E ne = (E) re.getValue().add(implementation, newOperations);
79                 mb.put(re.getKey(), ne);
80             } else {
81                 mb.put(re);
82             }
83         }
84
85         // Finally add whatever is left in the decomposed multimap
86         for (Entry<SchemaPath, Collection<D>> e : toAdd.asMap().entrySet()) {
87             final Builder<D, List<M>> vb = ImmutableMap.builder();
88             final List<M> v = ImmutableList.of(implementation);
89             for (D i : e.getValue()) {
90                 vb.put(i, v);
91             }
92
93             final E entry = createOperationEntry(schemaContext, e.getKey(),
94                 vb.build());
95             if (entry != null) {
96                 mb.put(e.getKey(), entry);
97             }
98         }
99
100         return newInstance(mb.build(), schemaContext);
101     }
102
103     AbstractDOMRoutingTable<I, D, M, L, E> remove(final M implementation, final Set<I> instances) {
104         if (instances.isEmpty()) {
105             return this;
106         }
107
108         // First decompose the identifiers to a multimap
109         final ListMultimap<SchemaPath, D> toRemove = decomposeIdentifiers(instances);
110
111         // Now iterate over existing entries, modifying them as appropriate...
112         final Builder<SchemaPath, E> b = ImmutableMap.builder();
113         for (Entry<SchemaPath, E> e : this.operations.entrySet()) {
114             final List<D> removed = new ArrayList<>(toRemove.removeAll(e.getKey()));
115             if (!removed.isEmpty()) {
116                 final E ne = (E) e.getValue().remove(implementation, removed);
117                 if (ne != null) {
118                     b.put(e.getKey(), ne);
119                 }
120             } else {
121                 b.put(e);
122             }
123         }
124
125         // All done, whatever is in toRemove, was not there in the first place
126         return newInstance(b.build(), schemaContext);
127     }
128
129     @VisibleForTesting
130     Map<SchemaPath, Set<D>> getOperations() {
131         return Maps.transformValues(operations, AbstractDOMRoutingTableEntry::registeredIdentifiers);
132     }
133
134     Map<SchemaPath, Set<D>> getOperations(final L listener) {
135         final Map<SchemaPath, Set<D>> ret = new HashMap<>(operations.size());
136         for (Entry<SchemaPath, E> e : operations.entrySet()) {
137             final Set<D> ids = e.getValue().registeredIdentifiers(listener);
138             if (!ids.isEmpty()) {
139                 ret.put(e.getKey(), ids);
140             }
141         }
142
143         return ret;
144     }
145
146     @Nullable AbstractDOMRoutingTableEntry<D, M, L> getEntry(final @NonNull SchemaPath type) {
147         return operations.get(type);
148     }
149
150     protected abstract AbstractDOMRoutingTable<I, D, M, L, E> newInstance(Map<SchemaPath, E> operations,
151             EffectiveModelContext schemaContext);
152
153     abstract ListMultimap<SchemaPath, D> decomposeIdentifiers(Set<I> instances);
154
155     abstract E createOperationEntry(EffectiveModelContext context, SchemaPath key, Map<D, List<M>> implementations);
156 }