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 Collection<Builder> inheritedOne;
51 private final YangInstanceIdentifier nodeId;
52 private final Collection<Node> nodes;
54 private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
55 private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
56 private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders;
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);
67 * Collect the nodes which need to be propagated from us to the child.
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()) {
89 baseBuilders = maybeEmpty(base);
90 oneBuilders = maybeEmpty(one);
91 subBuilders = maybeEmpty(sub);
94 private static <K, V> Map<K, V> maybeEmpty(final Map<K, V> map) {
96 return Collections.emptyMap();
102 * Create an initial state handle at a particular root node.
104 * @param rootId root instance identifier
105 * @param root root node
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));
114 * Create a state handle for iterating over a particular child.
116 * @param childId ID of the child
117 * @return State handle
119 public ResolveDataChangeState child(final PathArgument childId) {
121 * We instantiate a concatenation only when needed, otherwise
122 * we reuse the collection. This speeds up Iterables.isEmpty()
123 * in needsProcessing().
125 final Iterable<Builder> sb;
126 if (subBuilders.isEmpty()) {
129 sb = Iterables.concat(inheritedSub, subBuilders.values());
132 return new ResolveDataChangeState(nodeId.node(childId), sb,
133 oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
137 * Get the current path
139 * @return Current path.
141 public YangInstanceIdentifier getPath() {
146 * Check if this child needs processing.
148 * @return True if processing needs to occur, false otherwise.
150 public boolean needsProcessing() {
151 // May have underlying listeners, so we need to process
152 if (!nodes.isEmpty()) {
155 // Have ONE listeners
156 if (!inheritedOne.isEmpty()) {
159 // Have SUBTREE listeners
160 if (!Iterables.isEmpty(inheritedSub)) {
168 * Add an event to all current listeners.
172 public void addEvent(final DOMImmutableDataChangeEvent event) {
173 // Subtree builders get always notified
174 for (Builder b : subBuilders.values()) {
177 for (Builder b : inheritedSub) {
181 if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
182 for (Builder b : oneBuilders.values()) {
187 if (event.getScope() == DataChangeScope.BASE) {
188 for (Builder b : inheritedOne) {
191 for (Builder b : baseBuilders.values()) {
198 * Gather all non-empty events into the provided map.
200 * @param before before-image
201 * @param after after-image
202 * @param map target map
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();
209 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
212 for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
213 final Builder b = e.getValue();
215 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
218 for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
219 final Builder b = e.getValue();
221 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
225 LOG.trace("Collected events {}", map);
228 private static Collection<Node> getListenerChildrenWildcarded(final Collection<Node> parentNodes,
229 final PathArgument child) {
230 if (parentNodes.isEmpty()) {
231 return Collections.emptyList();
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);
239 addChildNodes(result, parentNodes, child);
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());