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