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