Cleaned up mdsal-common-api and mdsal-dom-spi
[mdsal.git] / dom / mdsal-dom-inmemory-datastore / src / main / java / org / opendaylight / mdsal / dom / store / inmemory / ResolveDataChangeState.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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.store.inmemory;
9
10 import org.opendaylight.mdsal.dom.store.inmemory.DOMImmutableDataChangeEvent.Builder;
11
12 import org.opendaylight.mdsal.dom.spi.RegistrationTreeNode;
13 import org.opendaylight.mdsal.common.api.AsyncDataBroker.DataChangeScope;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.Iterables;
16 import com.google.common.collect.Multimap;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Recursion state used in {@link ResolveDataChangeEventsTask}. Instances of this
35  * method track which listeners are affected by a particular change node. It takes
36  * care of properly inheriting SUB/ONE listeners and also provides a means to
37  * understand when actual processing need not occur.
38  */
39 final class ResolveDataChangeState {
40     private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class);
41     /**
42      * Inherited from all parents
43      */
44     private final Iterable<Builder> inheritedSub;
45     /**
46      * Inherited from immediate parent
47      */
48     private final Collection<Builder> inheritedOne;
49     private final YangInstanceIdentifier nodeId;
50     private final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes;
51
52     private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
53     private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
54     private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders;
55
56     private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
57             final Iterable<Builder> inheritedSub, final Collection<Builder> inheritedOne,
58             final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes) {
59         this.nodeId = Preconditions.checkNotNull(nodeId);
60         this.nodes = Preconditions.checkNotNull(nodes);
61         this.inheritedSub = Preconditions.checkNotNull(inheritedSub);
62         this.inheritedOne = Preconditions.checkNotNull(inheritedOne);
63
64         /*
65          * Collect the nodes which need to be propagated from us to the child.
66          */
67         final Map<DataChangeListenerRegistration<?>, Builder> sub = new HashMap<>();
68         final Map<DataChangeListenerRegistration<?>, Builder> one = new HashMap<>();
69         final Map<DataChangeListenerRegistration<?>, Builder> base = new HashMap<>();
70         for (RegistrationTreeNode<DataChangeListenerRegistration<?>> n : nodes) {
71             for (DataChangeListenerRegistration<?> l : n.getRegistrations()) {
72                 final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
73                 switch (l.getScope()) {
74                 case BASE:
75                     base.put(l, b);
76                     break;
77                 case ONE:
78                     one.put(l, b);
79                     break;
80                 case SUBTREE:
81                     sub.put(l, b);
82                     break;
83                 }
84             }
85         }
86
87         baseBuilders = maybeEmpty(base);
88         oneBuilders = maybeEmpty(one);
89         subBuilders = maybeEmpty(sub);
90     }
91
92     private static <K, V> Map<K, V> maybeEmpty(final Map<K, V> map) {
93         if (map.isEmpty()) {
94             return Collections.emptyMap();
95         }
96         return map;
97     }
98
99     /**
100      * Create an initial state handle at a particular root node.
101      *
102      * @param rootId root instance identifier
103      * @param registrationTreeNode root node
104      * @return
105      */
106     public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final RegistrationTreeNode<DataChangeListenerRegistration<?>> registrationTreeNode) {
107         return new ResolveDataChangeState(rootId, Collections.<Builder>emptyList(),
108             Collections.<Builder>emptyList(), Collections.singletonList(registrationTreeNode));
109     }
110
111     /**
112      * Create a state handle for iterating over a particular child.
113      *
114      * @param childId ID of the child
115      * @return State handle
116      */
117     public ResolveDataChangeState child(final PathArgument childId) {
118         /*
119          * We instantiate a concatenation only when needed:
120          *
121          * 1) If our collection is empty, we reuse the parent's. This is typically the case
122          *    for intermediate node, which should be the vast majority.
123          * 2) If the parent's iterable is a Collection and it is empty, reuse our collection.
124          *    This is the case for the first node which defines a subtree listener in a
125          *    particular subtree.
126          * 3) Concatenate the two collections. This happens when we already have some
127          *    subtree listeners and we encounter a node which adds a few more.
128          *
129          * This allows us to lower number of objects allocated and also
130          * speeds up Iterables.isEmpty() in needsProcessing().
131          *
132          * Note that the check for Collection in 2) relies on precisely this logic, which
133          * ensures that we simply cannot see an empty concatenation, but rather start off with
134          * an empty collection, then switch to a non-empty collection and finally switch to
135          * a concatenation. This saves us from instantiating iterators, which a trivial
136          * Iterables.isEmpty() would do as soon as we cross case 3).
137          */
138         final Iterable<Builder> sb;
139         if (!subBuilders.isEmpty()) {
140             if (inheritedSub instanceof Collection && ((Collection<?>) inheritedSub).isEmpty()) {
141                 sb = subBuilders.values();
142             } else {
143                 sb = Iterables.concat(inheritedSub, subBuilders.values());
144             }
145         } else {
146             sb = inheritedSub;
147         }
148
149         return new ResolveDataChangeState(nodeId.node(childId), sb,
150             oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
151     }
152
153     /**
154      * Get the current path
155      *
156      * @return Current path.
157      */
158     public YangInstanceIdentifier getPath() {
159         return nodeId;
160     }
161
162     /**
163      * Check if this child needs processing.
164      *
165      * @return True if processing needs to occur, false otherwise.
166      */
167     public boolean needsProcessing() {
168         // May have underlying listeners, so we need to process
169         if (!nodes.isEmpty()) {
170             return true;
171         }
172         // Have ONE listeners
173         if (!inheritedOne.isEmpty()) {
174             return true;
175         }
176
177         /*
178          * Have SUBTREE listeners
179          *
180          * This is slightly magical replacement for !Iterables.isEmpty(inheritedSub).
181          * It relies on the logic in child(), which gives us the guarantee that when
182          * inheritedSub is not a Collection, it is guaranteed to be non-empty (which
183          * means we need to process). If it is a collection, we still need to check
184          * it for emptiness.
185          *
186          * Unlike Iterables.isEmpty() this code does not instantiate any temporary
187          * objects and is thus more efficient.
188          */
189         if (inheritedSub instanceof Collection) {
190             return !((Collection<?>) inheritedSub).isEmpty();
191         }
192
193         // Non-Collection => non-empty => have to process
194         return true;
195     }
196
197     /**
198      * Add an event to all current listeners.
199      *
200      * @param event
201      */
202     public void addEvent(final DOMImmutableDataChangeEvent event) {
203         // Subtree builders get always notified
204         for (Builder b : subBuilders.values()) {
205             b.merge(event);
206         }
207         for (Builder b : inheritedSub) {
208             b.merge(event);
209         }
210
211         if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
212             for (Builder b : oneBuilders.values()) {
213                 b.merge(event);
214             }
215         }
216
217         if (event.getScope() == DataChangeScope.BASE) {
218             for (Builder b : inheritedOne) {
219                 b.merge(event);
220             }
221             for (Builder b : baseBuilders.values()) {
222                 b.merge(event);
223             }
224         }
225     }
226
227     /**
228      * Gather all non-empty events into the provided map.
229      *
230      * @param before before-image
231      * @param after after-image
232      * @param map target map
233      */
234     public void collectEvents(final NormalizedNode<?, ?> before, final NormalizedNode<?, ?> after,
235             final Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> map) {
236         for (Entry<DataChangeListenerRegistration<?>, Builder> e : baseBuilders.entrySet()) {
237             final Builder b = e.getValue();
238             if (!b.isEmpty()) {
239                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
240             }
241         }
242         for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
243             final Builder b = e.getValue();
244             if (!b.isEmpty()) {
245                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
246             }
247         }
248         for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
249             final Builder b = e.getValue();
250             if (!b.isEmpty()) {
251                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
252             }
253         }
254
255         LOG.trace("Collected events {}", map);
256     }
257
258     private static Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> getListenerChildrenWildcarded(final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes,
259             final PathArgument child) {
260         if (parentNodes.isEmpty()) {
261             return Collections.emptyList();
262         }
263
264         final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result = new ArrayList<>();
265         if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
266             NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
267             addChildNodes(result, parentNodes, wildcardedIdentifier);
268         }
269         addChildNodes(result, parentNodes, child);
270         return result;
271     }
272
273     private static void addChildNodes(final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result, final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes, final PathArgument childIdentifier) {
274         for (RegistrationTreeNode<DataChangeListenerRegistration<?>> node : parentNodes) {
275             RegistrationTreeNode<DataChangeListenerRegistration<?>> child = node.getExactChild(childIdentifier);
276             if (child != null) {
277                 result.add(child);
278             }
279         }
280     }
281 }