package org.opendaylight.controller.md.sal.dom.store.impl; import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder; import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.append; import static org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils.getChild; 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.ListenerRegistrationNode; import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification; import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode.DataChangeListenerRegistration; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; public class DataChangeEventResolver { private static final DOMImmutableDataChangeEvent NO_CHANGE = builder().build(); private InstanceIdentifier rootPath; private ListenerRegistrationNode listenerRoot; private NodeModification modificationRoot; private Optional beforeRoot; private Optional afterRoot; private final ImmutableList.Builder tasks = ImmutableList.builder(); protected InstanceIdentifier getRootPath() { return rootPath; } protected DataChangeEventResolver setRootPath(final InstanceIdentifier rootPath) { this.rootPath = rootPath; return this; } protected ListenerRegistrationNode getListenerRoot() { return listenerRoot; } protected DataChangeEventResolver setListenerRoot(final ListenerRegistrationNode listenerRoot) { this.listenerRoot = listenerRoot; return this; } protected NodeModification getModificationRoot() { return modificationRoot; } protected DataChangeEventResolver setModificationRoot(final NodeModification modificationRoot) { this.modificationRoot = modificationRoot; return this; } protected Optional getBeforeRoot() { return beforeRoot; } protected DataChangeEventResolver setBeforeRoot(final Optional beforeRoot) { this.beforeRoot = beforeRoot; return this; } protected Optional getAfterRoot() { return afterRoot; } protected DataChangeEventResolver setAfterRoot(final Optional afterRoot) { this.afterRoot = afterRoot; return this; } public Iterable resolve() { resolveAnyChangeEvent(rootPath, Optional.of(listenerRoot), modificationRoot, beforeRoot, afterRoot); return tasks.build(); } private DOMImmutableDataChangeEvent resolveAnyChangeEvent(final InstanceIdentifier path, final Optional listeners, final NodeModification modification, final Optional before, final Optional after) { // No listeners are present in listener registration subtree // no before and after state is present if (!before.isPresent() && !after.isPresent()) { return NO_CHANGE; } switch (modification.getModificationType()) { case SUBTREE_MODIFIED: return resolveSubtreeChangeEvent(path, listeners, modification, before.get(), after.get()); case WRITE: if (before.isPresent()) { return resolveReplacedEvent(path, listeners, modification, before.get(), after.get()); } else { return resolveCreateEvent(path, listeners, after.get()); } case DELETE: return resolveDeleteEvent(path, listeners, before.get()); default: return NO_CHANGE; } } /** * Resolves create events deep down the interest listener tree. * * * @param path * @param listeners * @param afterState * @return */ private DOMImmutableDataChangeEvent resolveCreateEvent(final InstanceIdentifier path, final Optional listeners, final StoreMetadataNode afterState) { final NormalizedNode node = afterState.getData(); Builder builder = builder().setAfter(node).addCreated(path, node); for (StoreMetadataNode child : afterState.getChildren()) { PathArgument childId = child.getIdentifier(); Optional childListeners = getChild(listeners, childId); InstanceIdentifier childPath = StoreUtils.append(path, childId); builder.merge(resolveCreateEvent(childPath, childListeners, child)); } DOMImmutableDataChangeEvent event = builder.build(); if (listeners.isPresent()) { addNotifyTask(listeners.get().getListeners(), event); } return event; } private DOMImmutableDataChangeEvent resolveDeleteEvent(final InstanceIdentifier path, final Optional listeners, final StoreMetadataNode beforeState) { final NormalizedNode node = beforeState.getData(); Builder builder = builder().setBefore(node).addRemoved(path, node); for (StoreMetadataNode child : beforeState.getChildren()) { PathArgument childId = child.getIdentifier(); Optional childListeners = getChild(listeners, childId); InstanceIdentifier childPath = StoreUtils.append(path, childId); builder.merge(resolveDeleteEvent(childPath, childListeners, child)); } DOMImmutableDataChangeEvent event = builder.build(); if (listeners.isPresent()) { addNotifyTask(listeners.get().getListeners(), event); } return event; } private DOMImmutableDataChangeEvent resolveSubtreeChangeEvent(final InstanceIdentifier path, final Optional listeners, final NodeModification modification, final StoreMetadataNode before, final StoreMetadataNode after) { Builder one = builder().setBefore(before.getData()).setAfter(after.getData()); Builder subtree = builder(); for (NodeModification childMod : modification.getModifications()) { PathArgument childId = childMod.getIdentifier(); InstanceIdentifier childPath = append(path, childId); Optional childListen = getChild(listeners, childId); Optional childBefore = before.getChild(childId); Optional childAfter = after.getChild(childId); switch (childMod.getModificationType()) { case WRITE: case DELETE: one.merge(resolveAnyChangeEvent(childPath, childListen, childMod, childBefore, childBefore)); break; case SUBTREE_MODIFIED: subtree.merge(resolveSubtreeChangeEvent(childPath, childListen, childMod, childBefore.get(), childAfter.get())); break; } } DOMImmutableDataChangeEvent oneChangeEvent = one.build(); subtree.merge(oneChangeEvent); DOMImmutableDataChangeEvent subtreeEvent = subtree.build(); if (listeners.isPresent()) { addNotifyTask(listeners.get(), DataChangeScope.ONE, oneChangeEvent); addNotifyTask(listeners.get(), DataChangeScope.SUBTREE, subtreeEvent); } return subtreeEvent; } private DOMImmutableDataChangeEvent resolveReplacedEvent(final InstanceIdentifier path, final Optional listeners, final NodeModification modification, final StoreMetadataNode before, final StoreMetadataNode after) { // FIXME Add task return builder().build(); } private void addNotifyTask(final ListenerRegistrationNode listenerRegistrationNode, final DataChangeScope one, final DOMImmutableDataChangeEvent event) { } private void addNotifyTask(final Iterable> listeners, final DOMImmutableDataChangeEvent event) { tasks .add(new ChangeListenerNotifyTask(ImmutableSet.copyOf(listeners),event)); } public static DataChangeEventResolver create() { return new DataChangeEventResolver(); } }