858087e2df02f6c511289472eb43548a5ca099ae
[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, otherwise
122          * we reuse the collection. This speeds up Iterables.isEmpty()
123          * in needsProcessing().
124          */
125         final Iterable<Builder> sb;
126         if (subBuilders.isEmpty()) {
127             sb = inheritedSub;
128         } else {
129             sb = Iterables.concat(inheritedSub, subBuilders.values());
130         }
131
132         return new ResolveDataChangeState(nodeId.node(childId), sb,
133             oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
134     }
135
136     /**
137      * Get the current path
138      *
139      * @return Current path.
140      */
141     public YangInstanceIdentifier getPath() {
142         return nodeId;
143     }
144
145     /**
146      * Check if this child needs processing.
147      *
148      * @return True if processing needs to occur, false otherwise.
149      */
150     public boolean needsProcessing() {
151         // May have underlying listeners, so we need to process
152         if (!nodes.isEmpty()) {
153             return true;
154         }
155         // Have ONE listeners
156         if (!inheritedOne.isEmpty()) {
157             return true;
158         }
159         // Have SUBTREE listeners
160         if (!Iterables.isEmpty(inheritedSub)) {
161             return true;
162         }
163
164         return false;
165     }
166
167     /**
168      * Add an event to all current listeners.
169      *
170      * @param event
171      */
172     public void addEvent(final DOMImmutableDataChangeEvent event) {
173         // Subtree builders get always notified
174         for (Builder b : subBuilders.values()) {
175             b.merge(event);
176         }
177         for (Builder b : inheritedSub) {
178             b.merge(event);
179         }
180
181         if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
182             for (Builder b : oneBuilders.values()) {
183                 b.merge(event);
184             }
185         }
186
187         if (event.getScope() == DataChangeScope.BASE) {
188             for (Builder b : inheritedOne) {
189                 b.merge(event);
190             }
191             for (Builder b : baseBuilders.values()) {
192                 b.merge(event);
193             }
194         }
195     }
196
197     /**
198      * Gather all non-empty events into the provided map.
199      *
200      * @param before before-image
201      * @param after after-image
202      * @param map target map
203      */
204     public void collectEvents(final NormalizedNode<?, ?> before, final NormalizedNode<?, ?> after,
205             final Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> map) {
206         for (Entry<DataChangeListenerRegistration<?>, Builder> e : baseBuilders.entrySet()) {
207             final Builder b = e.getValue();
208             if (!b.isEmpty()) {
209                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
210             }
211         }
212         for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
213             final Builder b = e.getValue();
214             if (!b.isEmpty()) {
215                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
216             }
217         }
218         for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
219             final Builder b = e.getValue();
220             if (!b.isEmpty()) {
221                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
222             }
223         }
224
225         LOG.trace("Collected events {}", map);
226     }
227
228     private static Collection<Node> getListenerChildrenWildcarded(final Collection<Node> parentNodes,
229             final PathArgument child) {
230         if (parentNodes.isEmpty()) {
231             return Collections.emptyList();
232         }
233
234         final List<Node> result = new ArrayList<>();
235         if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
236             NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
237             addChildNodes(result, parentNodes, wildcardedIdentifier);
238         }
239         addChildNodes(result, parentNodes, child);
240         return result;
241     }
242
243     private static void addChildNodes(final List<Node> result, final Collection<Node> parentNodes, final PathArgument childIdentifier) {
244         for (Node node : parentNodes) {
245             Optional<Node> child = node.getChild(childIdentifier);
246             if (child.isPresent()) {
247                 result.add(child.get());
248             }
249         }
250     }
251 }