2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.md.sal.dom.store.impl;
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;
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;
21 import java.util.Map.Entry;
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;
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.
41 final class ResolveDataChangeState {
42 private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class);
44 * Inherited from all parents
46 private final Iterable<Builder> inheritedSub;
48 * Inherited from immediate parent
50 private final Iterable<Builder> inheritedOne;
51 private final YangInstanceIdentifier nodeId;
52 private final Collection<Node> nodes;
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<>();
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);
67 * Collect the nodes which need to be propagated from us to the child.
69 for (Node n : nodes) {
70 for (DataChangeListenerRegistration<?> l : n.getListeners()) {
71 final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
72 switch (l.getScope()) {
74 baseBuilders.put(l, b);
77 oneBuilders.put(l, b);
80 subBuilders.put(l, b);
88 * Create an initial state handle at a particular root node.
90 * @param rootId root instance identifier
91 * @param root root node
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));
100 * Create a state handle for iterating over a particular child.
102 * @param childId ID of the child
103 * @return State handle
105 public ResolveDataChangeState child(final PathArgument childId) {
107 * We instantiate a concatenation only when needed, otherwise
108 * we reuse the collection. This speeds up Iterables.isEmpty()
109 * in needsProcessing().
111 final Iterable<Builder> sb;
112 if (subBuilders.isEmpty()) {
115 sb = Iterables.concat(inheritedSub, subBuilders.values());
118 return new ResolveDataChangeState(nodeId.node(childId), sb,
119 oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
123 * Get the current path
125 * @return Current path.
127 public YangInstanceIdentifier getPath() {
132 * Check if this child needs processing.
134 * @return True if processing needs to occur, false otherwise.
136 public boolean needsProcessing() {
137 // May have underlying listeners, so we need to process
138 if (!nodes.isEmpty()) {
141 // Have SUBTREE listeners
142 if (!Iterables.isEmpty(inheritedSub)) {
145 // Have ONE listeners
146 if (!Iterables.isEmpty(inheritedOne)) {
154 * Add an event to all current listeners.
158 public void addEvent(final DOMImmutableDataChangeEvent event) {
159 // Subtree builders get always notified
160 for (Builder b : subBuilders.values()) {
163 for (Builder b : inheritedSub) {
167 if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
168 for (Builder b : oneBuilders.values()) {
173 if (event.getScope() == DataChangeScope.BASE) {
174 for (Builder b : inheritedOne) {
177 for (Builder b : baseBuilders.values()) {
184 * Gather all non-empty events into the provided map.
186 * @param before before-image
187 * @param after after-image
188 * @param map target map
190 public void collectEvents(final NormalizedNode<?, ?> before, final NormalizedNode<?, ?> after,
191 final Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> map) {
192 for (Entry<DataChangeListenerRegistration<?>, Builder> e : baseBuilders.entrySet()) {
193 final Builder b = e.getValue();
195 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
198 for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
199 final Builder b = e.getValue();
201 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
204 for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
205 final Builder b = e.getValue();
207 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
211 LOG.trace("Collected events {}", map);
214 private static Collection<Node> getListenerChildrenWildcarded(final Collection<Node> parentNodes,
215 final PathArgument child) {
216 if (parentNodes.isEmpty()) {
217 return Collections.emptyList();
220 final List<Node> result = new ArrayList<>();
221 if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
222 NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
223 addChildNodes(result, parentNodes, wildcardedIdentifier);
225 addChildNodes(result, parentNodes, child);
229 private static void addChildNodes(final List<Node> result, final Collection<Node> parentNodes, final PathArgument childIdentifier) {
230 for (Node node : parentNodes) {
231 Optional<Node> child = node.getChild(childIdentifier);
232 if (child.isPresent()) {
233 result.add(child.get());