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