*/
package org.opendaylight.controller.md.sal.dom.store.impl;
-import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Node;
+import org.opendaylight.mdsal.dom.spi.RegistrationTreeNode;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
*/
final class ResolveDataChangeState {
private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class);
+
/**
- * Inherited from all parents
+ * Inherited from all parents.
*/
private final Iterable<Builder> inheritedSub;
+
/**
- * Inherited from immediate parent
+ * Inherited from immediate parent.
*/
- private final Iterable<Builder> inheritedOne;
+ private final Collection<Builder> inheritedOne;
private final YangInstanceIdentifier nodeId;
- private final Collection<Node> nodes;
+ private final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes;
- private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders = new HashMap<>();
- private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders = new HashMap<>();
- private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders = new HashMap<>();
+ private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
+ private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
+ private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders;
private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
- final Iterable<Builder> inheritedSub, final Iterable<Builder> inheritedOne,
- final Collection<Node> nodes) {
+ final Iterable<Builder> inheritedSub, final Collection<Builder> inheritedOne,
+ final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes) {
this.nodeId = Preconditions.checkNotNull(nodeId);
this.nodes = Preconditions.checkNotNull(nodes);
this.inheritedSub = Preconditions.checkNotNull(inheritedSub);
/*
* Collect the nodes which need to be propagated from us to the child.
*/
- for (Node n : nodes) {
- for (DataChangeListenerRegistration<?> l : n.getListeners()) {
+ final Map<DataChangeListenerRegistration<?>, Builder> sub = new HashMap<>();
+ final Map<DataChangeListenerRegistration<?>, Builder> one = new HashMap<>();
+ final Map<DataChangeListenerRegistration<?>, Builder> base = new HashMap<>();
+ for (RegistrationTreeNode<DataChangeListenerRegistration<?>> n : nodes) {
+ for (DataChangeListenerRegistration<?> l : n.getRegistrations()) {
final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
switch (l.getScope()) {
- case BASE:
- baseBuilders.put(l, b);
- break;
- case ONE:
- oneBuilders.put(l, b);
- break;
- case SUBTREE:
- subBuilders.put(l, b);
- break;
+ case BASE:
+ base.put(l, b);
+ break;
+ case ONE:
+ one.put(l, b);
+ break;
+ case SUBTREE:
+ sub.put(l, b);
+ break;
+ default:
+ break;
}
}
}
+
+ baseBuilders = maybeEmpty(base);
+ oneBuilders = maybeEmpty(one);
+ subBuilders = maybeEmpty(sub);
+ }
+
+ private static <K, V> Map<K, V> maybeEmpty(final Map<K, V> map) {
+ if (map.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ return map;
}
/**
* Create an initial state handle at a particular root node.
*
* @param rootId root instance identifier
- * @param root root node
- * @return
+ * @param registrationTreeNode root node
*/
- public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final Node root) {
+ public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId,
+ final RegistrationTreeNode<DataChangeListenerRegistration<?>> registrationTreeNode) {
return new ResolveDataChangeState(rootId, Collections.<Builder>emptyList(),
- Collections.<Builder>emptyList(), Collections.singletonList(root));
+ Collections.<Builder>emptyList(), Collections.singletonList(registrationTreeNode));
}
/**
* @return State handle
*/
public ResolveDataChangeState child(final PathArgument childId) {
- return new ResolveDataChangeState(nodeId.node(childId),
- Iterables.concat(inheritedSub, subBuilders.values()),
+ /*
+ * We instantiate a concatenation only when needed:
+ *
+ * 1) If our collection is empty, we reuse the parent's. This is typically the case
+ * for intermediate node, which should be the vast majority.
+ * 2) If the parent's iterable is a Collection and it is empty, reuse our collection.
+ * This is the case for the first node which defines a subtree listener in a
+ * particular subtree.
+ * 3) Concatenate the two collections. This happens when we already have some
+ * subtree listeners and we encounter a node which adds a few more.
+ *
+ * This allows us to lower number of objects allocated and also
+ * speeds up Iterables.isEmpty() in needsProcessing().
+ *
+ * Note that the check for Collection in 2) relies on precisely this logic, which
+ * ensures that we simply cannot see an empty concatenation, but rather start off with
+ * an empty collection, then switch to a non-empty collection and finally switch to
+ * a concatenation. This saves us from instantiating iterators, which a trivial
+ * Iterables.isEmpty() would do as soon as we cross case 3).
+ */
+ final Iterable<Builder> sb;
+ if (!subBuilders.isEmpty()) {
+ if (inheritedSub instanceof Collection && ((Collection<?>) inheritedSub).isEmpty()) {
+ sb = subBuilders.values();
+ } else {
+ sb = Iterables.concat(inheritedSub, subBuilders.values());
+ }
+ } else {
+ sb = inheritedSub;
+ }
+
+ return new ResolveDataChangeState(nodeId.node(childId), sb,
oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
}
/**
- * Get the current path
+ * Get the current path.
*
* @return Current path.
*/
if (!nodes.isEmpty()) {
return true;
}
- // Have SUBTREE listeners
- if (!Iterables.isEmpty(inheritedSub)) {
- return true;
- }
// Have ONE listeners
- if (!Iterables.isEmpty(inheritedOne)) {
+ if (!inheritedOne.isEmpty()) {
return true;
}
- // FIXME: do we need anything else? If not, flip this to 'false'
+ /*
+ * Have SUBTREE listeners
+ *
+ * This is slightly magical replacement for !Iterables.isEmpty(inheritedSub).
+ * It relies on the logic in child(), which gives us the guarantee that when
+ * inheritedSub is not a Collection, it is guaranteed to be non-empty (which
+ * means we need to process). If it is a collection, we still need to check
+ * it for emptiness.
+ *
+ * Unlike Iterables.isEmpty() this code does not instantiate any temporary
+ * objects and is thus more efficient.
+ */
+ if (inheritedSub instanceof Collection) {
+ return !((Collection<?>) inheritedSub).isEmpty();
+ }
+
+ // Non-Collection => non-empty => have to process
return true;
}
/**
* Add an event to all current listeners.
- *
- * @param event
*/
public void addEvent(final DOMImmutableDataChangeEvent event) {
// Subtree builders get always notified
LOG.trace("Collected events {}", map);
}
- private static Collection<Node> getListenerChildrenWildcarded(final Collection<Node> parentNodes,
+ private static Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> getListenerChildrenWildcarded(
+ final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes,
final PathArgument child) {
if (parentNodes.isEmpty()) {
return Collections.emptyList();
}
- final List<Node> result = new ArrayList<>();
+ final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result = new ArrayList<>();
if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
addChildNodes(result, parentNodes, wildcardedIdentifier);
return result;
}
- private static void addChildNodes(final List<Node> result, final Collection<Node> parentNodes, final PathArgument childIdentifier) {
- for (Node node : parentNodes) {
- Optional<Node> child = node.getChild(childIdentifier);
- if (child.isPresent()) {
- result.add(child.get());
+ private static void addChildNodes(final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result,
+ final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes,
+ final PathArgument childIdentifier) {
+ for (RegistrationTreeNode<DataChangeListenerRegistration<?>> node : parentNodes) {
+ RegistrationTreeNode<DataChangeListenerRegistration<?>> child = node.getExactChild(childIdentifier);
+ if (child != null) {
+ result.add(child);
}
}
}