BUG-2673: Create AbstractRegistrationTree and related classes
[controller.git] / opendaylight / md-sal / sal-dom-spi / src / main / java / org / opendaylight / controller / md / sal / dom / spi / RegistrationTreeNode.java
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeNode.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/RegistrationTreeNode.java
new file mode 100644 (file)
index 0000000..41e80ea
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a single node within the registration tree. Note that the data returned from
+ * and instance of this class is guaranteed to have any relevance or consistency
+ * only as long as the {@link RegistrationTreeSnapshot} instance through which it is reached
+ * remains unclosed.
+ *
+ * @param <T> registration type
+ * @author Robert Varga
+ */
+public final class RegistrationTreeNode<T> implements Identifiable<PathArgument> {
+    private static final Logger LOG = LoggerFactory.getLogger(RegistrationTreeNode.class);
+
+    private final Map<PathArgument, RegistrationTreeNode<T>> children = new HashMap<>();
+    private final Collection<T> registrations = new ArrayList<>(2);
+    private final Collection<T> publicRegistrations = Collections.unmodifiableCollection(registrations);
+    private final Reference<RegistrationTreeNode<T>> parent;
+    private final PathArgument identifier;
+
+    RegistrationTreeNode(final RegistrationTreeNode<T> parent, final PathArgument identifier) {
+        this.parent = new WeakReference<>(parent);
+        this.identifier = identifier;
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * Return the child matching a {@link PathArgument} specification.
+     *
+     * @param arg Child identifier
+     * @return Child matching exactly, or null.
+     */
+    public RegistrationTreeNode<T> getExactChild(@Nonnull final PathArgument arg) {
+        return children.get(Preconditions.checkNotNull(arg));
+    }
+
+    /**
+     * Return a collection children which match a {@link PathArgument} specification inexactly.
+     * This explicitly excludes the child returned by {@link #getExactChild(PathArgument)}.
+     *
+     * @param arg Child identifier
+     * @return Collection of children, guaranteed to be non-null.
+     */
+    public @Nonnull Collection<RegistrationTreeNode<T>> getInexactChildren(@Nonnull final PathArgument arg) {
+        Preconditions.checkNotNull(arg);
+        if (arg instanceof NodeWithValue || arg instanceof NodeIdentifierWithPredicates) {
+            /*
+             * TODO: This just all-or-nothing wildcards, which we have historically supported. Given
+             *       that the argument is supposed to have all the elements filled out, we could support
+             *       partial wildcards by iterating over the registrations and matching the maps for
+             *       partial matches.
+             */
+            final RegistrationTreeNode<T> child = children.get(new NodeIdentifier(arg.getNodeType()));
+            if (child == null) {
+                return Collections.emptyList();
+            } else {
+                return Collections.singletonList(child);
+            }
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    public Collection<T> getRegistrations() {
+        return publicRegistrations;
+    }
+
+    RegistrationTreeNode<T> ensureChild(@Nonnull final PathArgument child) {
+        RegistrationTreeNode<T> potential = children.get(Preconditions.checkNotNull(child));
+        if (potential == null) {
+            potential = new RegistrationTreeNode<T>(this, child);
+            children.put(child, potential);
+        }
+        return potential;
+    }
+
+    void addRegistration(@Nonnull final T registration) {
+        registrations.add(Preconditions.checkNotNull(registration));
+        LOG.debug("Registration {} added", registration);
+    }
+
+    void removeRegistration(@Nonnull final T registration) {
+        registrations.remove(Preconditions.checkNotNull(registration));
+        LOG.debug("Registration {} removed", registration);
+
+        // We have been called with the write-lock held, so we can perform some cleanup.
+        removeThisIfUnused();
+    }
+
+    private void removeThisIfUnused() {
+        final RegistrationTreeNode<T> p = parent.get();
+        if (p != null && registrations.isEmpty() && children.isEmpty()) {
+            p.removeChild(identifier);
+        }
+    }
+
+    private void removeChild(final PathArgument arg) {
+        children.remove(arg);
+        removeThisIfUnused();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("identifier", identifier)
+                .add("registrations", registrations.size())
+                .add("children", children.size()).toString();
+    }
+}