f89dce93d62625f6bcae7838c485db7a59698545
[mdsal.git] / dom / mdsal-dom-spi / src / main / java / org / opendaylight / mdsal / dom / spi / RegistrationTreeNode.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;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects;
13 import java.lang.ref.Reference;
14 import java.lang.ref.WeakReference;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.Map;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.concepts.Identifiable;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * This is a single node within the registration tree. Note that the data returned from
31  * and instance of this class is guaranteed to have any relevance or consistency
32  * only as long as the {@link RegistrationTreeSnapshot} instance through which it is reached
33  * remains unclosed.
34  *
35  * @param <T> registration type
36  * @author Robert Varga
37  */
38 public final class RegistrationTreeNode<T> implements Identifiable<PathArgument> {
39     private static final Logger LOG = LoggerFactory.getLogger(RegistrationTreeNode.class);
40
41     private final Map<PathArgument, RegistrationTreeNode<T>> children = new HashMap<>();
42     private final Collection<T> registrations = new ArrayList<>(2);
43     private final Collection<T> publicRegistrations = Collections.unmodifiableCollection(registrations);
44     private final Reference<RegistrationTreeNode<T>> parent;
45     private final PathArgument identifier;
46
47     RegistrationTreeNode(final RegistrationTreeNode<T> parent, final PathArgument identifier) {
48         this.parent = new WeakReference<>(parent);
49         this.identifier = identifier;
50     }
51
52     @Override
53     public PathArgument getIdentifier() {
54         return identifier;
55     }
56
57     /**
58      * Return the child matching a {@link PathArgument} specification.
59      *
60      * @param arg Child identifier
61      * @return Child matching exactly, or null.
62      */
63     public RegistrationTreeNode<T> getExactChild(final @NonNull PathArgument arg) {
64         return children.get(requireNonNull(arg));
65     }
66
67     /**
68      * Return a collection children which match a {@link PathArgument} specification inexactly.
69      * This explicitly excludes the child returned by {@link #getExactChild(PathArgument)}.
70      *
71      * @param arg Child identifier
72      * @return Collection of children, guaranteed to be non-null.
73      */
74     public @NonNull Collection<RegistrationTreeNode<T>> getInexactChildren(final @NonNull PathArgument arg) {
75         requireNonNull(arg);
76         if (arg instanceof NodeWithValue || arg instanceof NodeIdentifierWithPredicates) {
77             /*
78              * TODO: This just all-or-nothing wildcards, which we have historically supported. Given
79              *       that the argument is supposed to have all the elements filled out, we could support
80              *       partial wildcards by iterating over the registrations and matching the maps for
81              *       partial matches.
82              */
83             final RegistrationTreeNode<T> child = children.get(new NodeIdentifier(arg.getNodeType()));
84             if (child == null) {
85                 return Collections.emptyList();
86             }
87
88             return Collections.singletonList(child);
89         }
90
91         return Collections.emptyList();
92     }
93
94     public Collection<T> getRegistrations() {
95         return publicRegistrations;
96     }
97
98     RegistrationTreeNode<T> ensureChild(final @NonNull PathArgument child) {
99         RegistrationTreeNode<T> potential = children.get(requireNonNull(child));
100         if (potential == null) {
101             potential = new RegistrationTreeNode<>(this, child);
102             children.put(child, potential);
103         }
104         return potential;
105     }
106
107     void addRegistration(final @NonNull T registration) {
108         registrations.add(requireNonNull(registration));
109         LOG.debug("Registration {} added", registration);
110     }
111
112     void removeRegistration(final @NonNull T registration) {
113         registrations.remove(requireNonNull(registration));
114         LOG.debug("Registration {} removed", registration);
115
116         // We have been called with the write-lock held, so we can perform some cleanup.
117         removeThisIfUnused();
118     }
119
120     private void removeThisIfUnused() {
121         final RegistrationTreeNode<T> p = parent.get();
122         if (p != null && registrations.isEmpty() && children.isEmpty()) {
123             p.removeChild(identifier);
124         }
125     }
126
127     private void removeChild(final PathArgument arg) {
128         children.remove(arg);
129         removeThisIfUnused();
130     }
131
132     @Override
133     public String toString() {
134         return MoreObjects.toStringHelper(this)
135                 .add("identifier", identifier)
136                 .add("registrations", registrations.size())
137                 .add("children", children.size()).toString();
138     }
139 }