/** * 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 registration type * @author Robert Varga */ public final class RegistrationTreeNode implements Identifiable { private static final Logger LOG = LoggerFactory.getLogger(RegistrationTreeNode.class); private final Map> children = new HashMap<>(); private final Collection registrations = new ArrayList<>(2); private final Collection publicRegistrations = Collections.unmodifiableCollection(registrations); private final Reference> parent; private final PathArgument identifier; RegistrationTreeNode(final RegistrationTreeNode 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 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> 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 child = children.get(new NodeIdentifier(arg.getNodeType())); if (child == null) { return Collections.emptyList(); } else { return Collections.singletonList(child); } } else { return Collections.emptyList(); } } public Collection getRegistrations() { return publicRegistrations; } RegistrationTreeNode ensureChild(@Nonnull final PathArgument child) { RegistrationTreeNode potential = children.get(Preconditions.checkNotNull(child)); if (potential == null) { potential = new RegistrationTreeNode<>(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 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(); } }