BUG-1493: split off recursion tracking and rework it
[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 Iterable<Builder> inheritedOne;
51     private final YangInstanceIdentifier nodeId;
52     private final Collection<Node> nodes;
53
54     private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders = new HashMap<>();
55     private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders = new HashMap<>();
56     private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders = new HashMap<>();
57
58     private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
59             final Iterable<Builder> inheritedSub, final Iterable<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         for (Node n : nodes) {
70             for (DataChangeListenerRegistration<?> l : n.getListeners()) {
71                 final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
72                 switch (l.getScope()) {
73                 case BASE:
74                     baseBuilders.put(l, b);
75                     break;
76                 case ONE:
77                     oneBuilders.put(l, b);
78                     break;
79                 case SUBTREE:
80                     subBuilders.put(l, b);
81                     break;
82                 }
83             }
84         }
85     }
86
87     /**
88      * Create an initial state handle at a particular root node.
89      *
90      * @param rootId root instance identifier
91      * @param root root node
92      * @return
93      */
94     public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final Node root) {
95         return new ResolveDataChangeState(rootId, Collections.<Builder>emptyList(),
96             Collections.<Builder>emptyList(), Collections.singletonList(root));
97     }
98
99     /**
100      * Create a state handle for iterating over a particular child.
101      *
102      * @param childId ID of the child
103      * @return State handle
104      */
105     public ResolveDataChangeState child(final PathArgument childId) {
106         return new ResolveDataChangeState(nodeId.node(childId),
107             Iterables.concat(inheritedSub, subBuilders.values()),
108             oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
109     }
110
111     /**
112      * Get the current path
113      *
114      * @return Current path.
115      */
116     public YangInstanceIdentifier getPath() {
117         return nodeId;
118     }
119
120     /**
121      * Check if this child needs processing.
122      *
123      * @return True if processing needs to occur, false otherwise.
124      */
125     public boolean needsProcessing() {
126         // May have underlying listeners, so we need to process
127         if (!nodes.isEmpty()) {
128             return true;
129         }
130         // Have SUBTREE listeners
131         if (!Iterables.isEmpty(inheritedSub)) {
132             return true;
133         }
134         // Have ONE listeners
135         if (!Iterables.isEmpty(inheritedOne)) {
136             return true;
137         }
138
139         // FIXME: do we need anything else? If not, flip this to 'false'
140         return true;
141     }
142
143     /**
144      * Add an event to all current listeners.
145      *
146      * @param event
147      */
148     public void addEvent(final DOMImmutableDataChangeEvent event) {
149         // Subtree builders get always notified
150         for (Builder b : subBuilders.values()) {
151             b.merge(event);
152         }
153         for (Builder b : inheritedSub) {
154             b.merge(event);
155         }
156
157         if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
158             for (Builder b : oneBuilders.values()) {
159                 b.merge(event);
160             }
161         }
162
163         if (event.getScope() == DataChangeScope.BASE) {
164             for (Builder b : inheritedOne) {
165                 b.merge(event);
166             }
167             for (Builder b : baseBuilders.values()) {
168                 b.merge(event);
169             }
170         }
171     }
172
173     /**
174      * Gather all non-empty events into the provided map.
175      *
176      * @param before before-image
177      * @param after after-image
178      * @param map target map
179      */
180     public void collectEvents(final NormalizedNode<?, ?> before, final NormalizedNode<?, ?> after,
181             final Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> map) {
182         for (Entry<DataChangeListenerRegistration<?>, Builder> e : baseBuilders.entrySet()) {
183             final Builder b = e.getValue();
184             if (!b.isEmpty()) {
185                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
186             }
187         }
188         for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
189             final Builder b = e.getValue();
190             if (!b.isEmpty()) {
191                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
192             }
193         }
194         for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
195             final Builder b = e.getValue();
196             if (!b.isEmpty()) {
197                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
198             }
199         }
200
201         LOG.trace("Collected events {}", map);
202     }
203
204     private static Collection<Node> getListenerChildrenWildcarded(final Collection<Node> parentNodes,
205             final PathArgument child) {
206         if (parentNodes.isEmpty()) {
207             return Collections.emptyList();
208         }
209
210         final List<Node> result = new ArrayList<>();
211         if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
212             NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
213             addChildNodes(result, parentNodes, wildcardedIdentifier);
214         }
215         addChildNodes(result, parentNodes, child);
216         return result;
217     }
218
219     private static void addChildNodes(final List<Node> result, final Collection<Node> parentNodes, final PathArgument childIdentifier) {
220         for (Node node : parentNodes) {
221             Optional<Node> child = node.getChild(childIdentifier);
222             if (child.isPresent()) {
223                 result.add(child.get());
224             }
225         }
226     }
227 }

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.