Clean up AbstractRegistrationTree
[mdsal.git] / dom / mdsal-dom-spi / src / main / java / org / opendaylight / mdsal / dom / spi / store / AbstractDOMStoreTreeChangePublisher.java
1 /*
2  * Copyright (c) 2015 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.mdsal.dom.spi.store;
9
10 import com.google.common.collect.ImmutableList;
11 import com.google.common.collect.ListMultimap;
12 import com.google.common.collect.Multimap;
13 import com.google.common.collect.Multimaps;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.IdentityHashMap;
17 import java.util.List;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
20 import org.opendaylight.mdsal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
21 import org.opendaylight.mdsal.dom.spi.AbstractRegistrationTree;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
24 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
25 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
26 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
27 import org.opendaylight.yangtools.yang.data.tree.spi.DataTreeCandidates;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * Abstract base class for {@link DOMStoreTreeChangePublisher} implementations.
33  */
34 public abstract class AbstractDOMStoreTreeChangePublisher
35     extends AbstractRegistrationTree<AbstractDOMDataTreeChangeListenerRegistration<?>>
36         implements DOMStoreTreeChangePublisher {
37     private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMStoreTreeChangePublisher.class);
38
39     /**
40      * Callback for subclass to notify a specified registration of a list of candidates. This method is guaranteed
41      * to be only called from within {@link #processCandidateTree(DataTreeCandidate)}.
42      * @param registration the registration to notify
43      * @param changes the list of DataTreeCandidate changes
44      */
45     protected abstract void notifyListener(@NonNull AbstractDOMDataTreeChangeListenerRegistration<?> registration,
46             @NonNull List<DataTreeCandidate> changes);
47
48     /**
49      * Callback notifying the subclass that the specified registration is being
50      * closed and it's user no longer
51      * wishes to receive notifications. This notification is invoked while
52      * the {@link org.opendaylight.yangtools.concepts.ListenerRegistration#close()}
53      * method is executing. Subclasses can use this callback to properly
54      * remove any delayed notifications pending
55      * towards the registration.
56      *
57      * @param registration Registration which is being closed
58      */
59     protected abstract void registrationRemoved(
60             @NonNull AbstractDOMDataTreeChangeListenerRegistration<?> registration);
61
62     /**
63      * Process a candidate tree with respect to registered listeners.
64      *
65      * @param candidate candidate three which needs to be processed
66      * @return true if at least one listener was notified or false.
67      */
68     protected final boolean processCandidateTree(final @NonNull DataTreeCandidate candidate) {
69         final DataTreeCandidateNode node = candidate.getRootNode();
70         if (node.modificationType() == ModificationType.UNMODIFIED) {
71             LOG.debug("Skipping unmodified candidate {}", candidate);
72             return false;
73         }
74
75         try (var snapshot = takeSnapshot()) {
76             final List<PathArgument> toLookup = ImmutableList.copyOf(candidate.getRootPath().getPathArguments());
77             final ListMultimap<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> listenerChanges =
78                     Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new);
79             lookupAndNotify(toLookup, 0, snapshot.getRootNode(), candidate, listenerChanges);
80
81             for (var entry : Multimaps.asMap(listenerChanges).entrySet()) {
82                 notifyListener(entry.getKey(), entry.getValue());
83             }
84
85             return !listenerChanges.isEmpty();
86         }
87     }
88
89     @Override
90     public <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L>
91         registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
92         // Take the write lock
93         takeLock();
94         try {
95             final Node<AbstractDOMDataTreeChangeListenerRegistration<?>> node =
96                     findNodeFor(treeId.getPathArguments());
97             final var reg = new AbstractDOMDataTreeChangeListenerRegistration<>(listener) {
98                 @Override
99                 protected void removeRegistration() {
100                     AbstractDOMStoreTreeChangePublisher.this.removeRegistration(node, this);
101                     registrationRemoved(this);
102                 }
103             };
104
105             addRegistration(node, reg);
106             return reg;
107         } finally {
108             // Always release the lock
109             releaseLock();
110         }
111     }
112
113     private void lookupAndNotify(final List<PathArgument> args,
114             final int offset, final Node<AbstractDOMDataTreeChangeListenerRegistration<?>> node,
115             final DataTreeCandidate candidate,
116             final Multimap<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> listenerChanges) {
117         if (args.size() != offset) {
118             final PathArgument arg = args.get(offset);
119
120             final Node<AbstractDOMDataTreeChangeListenerRegistration<?>> exactChild
121                 = node.getExactChild(arg);
122             if (exactChild != null) {
123                 lookupAndNotify(args, offset + 1, exactChild, candidate, listenerChanges);
124             }
125
126             for (Node<AbstractDOMDataTreeChangeListenerRegistration<?>> c :
127                     node.getInexactChildren(arg)) {
128                 lookupAndNotify(args, offset + 1, c, candidate, listenerChanges);
129             }
130         } else {
131             notifyNode(candidate.getRootPath(), node, candidate.getRootNode(), listenerChanges);
132         }
133     }
134
135     private void notifyNode(final YangInstanceIdentifier path,
136             final Node<AbstractDOMDataTreeChangeListenerRegistration<?>> regNode,
137             final DataTreeCandidateNode candNode,
138             final Multimap<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> listenerChanges) {
139         if (candNode.modificationType() == ModificationType.UNMODIFIED) {
140             LOG.debug("Skipping unmodified candidate {}", path);
141             return;
142         }
143
144         final var regs = regNode.getRegistrations();
145         if (!regs.isEmpty()) {
146             addToListenerChanges(regs, path, candNode, listenerChanges);
147         }
148
149         for (var candChild : candNode.childNodes()) {
150             if (candChild.modificationType() != ModificationType.UNMODIFIED) {
151                 final var regChild = regNode.getExactChild(candChild.name());
152                 if (regChild != null) {
153                     notifyNode(path.node(candChild.name()), regChild, candChild, listenerChanges);
154                 }
155
156                 for (var rc : regNode.getInexactChildren(candChild.name())) {
157                     notifyNode(path.node(candChild.name()), rc, candChild, listenerChanges);
158                 }
159             }
160         }
161     }
162
163     private static void addToListenerChanges(
164             final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations,
165             final YangInstanceIdentifier path, final DataTreeCandidateNode node,
166             final Multimap<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> listenerChanges) {
167         final DataTreeCandidate dataTreeCandidate = DataTreeCandidates.newDataTreeCandidate(path, node);
168
169         for (AbstractDOMDataTreeChangeListenerRegistration<?> reg : registrations) {
170             listenerChanges.put(reg, dataTreeCandidate);
171         }
172     }
173 }