Merge "Bug 2933: Make DataTreeModification and Listeners type-safe"
authorTom Pantelis <tpanteli@brocade.com>
Tue, 7 Apr 2015 18:16:32 +0000 (18:16 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 7 Apr 2015 18:16:33 +0000 (18:16 +0000)
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeServiceAdapter.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataTreeChangeListenerRegistration.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataObjectModification.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/LazyDataTreeModification.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/DataTreeChangeListenerTest.java

index 2eee0e8099923993e7ca57dfd89747a103c4cd0e..678ac34e39d25d54fc12316fc17df1d51c5ed28f 100644 (file)
@@ -10,8 +10,11 @@ package org.opendaylight.controller.md.sal.binding.api;
 import java.util.Collection;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 
 /**
@@ -20,7 +23,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
  * Represents modification of Data Object.
  *
  */
-public interface DataObjectModification<T extends DataObject> extends Identifiable<PathArgument> {
+public interface DataObjectModification<T extends DataObject> extends org.opendaylight.yangtools.concepts.Identifiable<PathArgument> {
 
     enum ModificationType {
         /**
@@ -76,5 +79,56 @@ public interface DataObjectModification<T extends DataObject> extends Identifiab
      */
     @Nonnull Collection<DataObjectModification<? extends DataObject>> getModifiedChildren();
 
+    /**
+     * Returns container child modification if {@code child} was modified by this
+     * modification.
+     *
+     * For accessing all modified list items consider iterating over {@link #getModifiedChildren()}.
+     *
+     * @param child Type of child - must be only container
+     * @return Modification of {@code child} if {@code child} was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied {@code child} class is not valid child according
+     *         to generated model.
+     */
+    @Nullable <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(@Nonnull Class<C> child);
+
+    /**
+     * Returns augmentation child modification if {@code augmentation} was modified by this
+     * modification.
+     *
+     * For accessing all modified list items consider iterating over {@link #getModifiedChildren()}.
+     *
+     * @param augmentation Type of augmentation - must be only container
+     * @return Modification of {@code augmentation} if {@code augmentation} was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied {@code augmentation} class is not valid augmentation
+     *         according to generated model.
+     */
+    @Nullable <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(@Nonnull Class<C> augmentation);
+
+
+    /**
+     * Returns child list item modification if {@code child} was modified by this modification.
+     *
+     * @param listItem Type of list item - must be list item with key
+     * @param listKey List item key
+     * @return Modification of {@code child} if {@code child} was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied {@code listItem} class is not valid child according
+     *         to generated model.
+     */
+    <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(
+            @Nonnull Class<C> listItem,@Nonnull  K listKey);
+
+    /**
+     * Returns a child modification if a node identified by {@code childArgument} was modified by
+     * this modification.
+     *
+     * @param childArgument Path Argument of child node
+     * @return Modification of child identified by {@code childArgument} if {@code childArgument}
+     *         was modified, null otherwise.
+     * @throws IllegalArgumentException If supplied path argument is not valid child according to
+     *         generated model.
+     *
+     */
+    @Nullable DataObjectModification<? extends DataObject> getModifiedChild(PathArgument childArgument);
 
 }
index 6b1df719ac672f12c24bbe167b9094772ed9c4a9..93ab968451be9882e91379c867ae11a13dc111ad 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.controller.md.sal.binding.api;
 import java.util.Collection;
 import java.util.EventListener;
 import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
 
 /**
  * Interface implemented by classes interested in receiving notifications about
@@ -17,7 +18,7 @@ import javax.annotation.Nonnull;
  * in that it provides a cursor-based view of the change, which has potentially
  * lower overhead and allow more flexible consumption of change event.
  */
-public interface DataTreeChangeListener extends EventListener {
+public interface DataTreeChangeListener<T extends DataObject> extends EventListener {
     /**
      * Invoked when there was data change for the supplied path, which was used
      * to register this listener.
@@ -39,5 +40,5 @@ public interface DataTreeChangeListener extends EventListener {
      *
      * @param changes Collection of change events, may not be null or empty.
      */
-    void onDataTreeChanged(@Nonnull Collection<DataTreeModification> changes);
+    void onDataTreeChanged(@Nonnull Collection<DataTreeModification<T>> changes);
 }
index ae4e36f14acb31653396d014facd18617f637e35..9d12e449169d74e89663f727ad37476582f2065d 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.controller.md.sal.binding.api;
 
 import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
 
 /**
  * A {@link DOMService} which allows users to register for changes to a
@@ -50,5 +51,5 @@ public interface DataTreeChangeService extends BindingService {
      *         your listener using {@link ListenerRegistration#close()} to stop
      *         delivery of change events.
      */
-    @Nonnull <L extends DataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(@Nonnull DataTreeIdentifier treeId, @Nonnull L listener);
+    @Nonnull <T extends DataObject,L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(@Nonnull DataTreeIdentifier<T> treeId, @Nonnull L listener);
 }
\ No newline at end of file
index 428957e988869d1b0641c42fa21bdc739b767993..c1c23d5e6fc7d3d17f24c9a7613375fb6b4881e3 100644 (file)
@@ -13,18 +13,19 @@ import javax.annotation.Nonnull;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 /**
  * A unique identifier for a particular subtree. It is composed of the logical
  * data store type and the instance identifier of the root node.
  */
-public final class DataTreeIdentifier implements Immutable, Path<DataTreeIdentifier>, Serializable {
+public final class DataTreeIdentifier<T extends DataObject> implements Immutable, Path<DataTreeIdentifier<?>>, Serializable {
     private static final long serialVersionUID = 1L;
-    private final InstanceIdentifier<?> rootIdentifier;
+    private final InstanceIdentifier<T> rootIdentifier;
     private final LogicalDatastoreType datastoreType;
 
-    public DataTreeIdentifier(final LogicalDatastoreType datastoreType, final InstanceIdentifier<?> rootIdentifier) {
+    public DataTreeIdentifier(final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> rootIdentifier) {
         this.datastoreType = Preconditions.checkNotNull(datastoreType);
         this.rootIdentifier = Preconditions.checkNotNull(rootIdentifier);
     }
@@ -48,7 +49,7 @@ public final class DataTreeIdentifier implements Immutable, Path<DataTreeIdentif
     }
 
     @Override
-    public boolean contains(final DataTreeIdentifier other) {
+    public boolean contains(final DataTreeIdentifier<?> other) {
         return datastoreType == other.datastoreType && rootIdentifier.contains(other.rootIdentifier);
     }
 
@@ -69,7 +70,7 @@ public final class DataTreeIdentifier implements Immutable, Path<DataTreeIdentif
         if (!(obj instanceof DataTreeIdentifier)) {
             return false;
         }
-        DataTreeIdentifier other = (DataTreeIdentifier) obj;
+        final DataTreeIdentifier<?> other = (DataTreeIdentifier<?>) obj;
         if (datastoreType != other.datastoreType) {
             return false;
         }
index aac51a6a4cfa349d4c5bf602c71c12a169dd8395..8163baceac95b449fdc57c50ad81c2d7b2322ddb 100644 (file)
@@ -17,7 +17,7 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
  * @author Tony Tkacik &lt;ttkacik@cisco.com&gt;
  *
  */
-public interface DataTreeModification {
+public interface DataTreeModification<T extends DataObject> {
 
     /**
      * Get the modification root path. This is the path of the root node
@@ -25,13 +25,13 @@ public interface DataTreeModification {
      *
      * @return absolute path of the root node
      */
-    @Nonnull DataTreeIdentifier getRootPath();
+    @Nonnull DataTreeIdentifier<T> getRootPath();
 
     /**
      * Get the modification root node.
      *
      * @return modification root node
      */
-    @Nonnull DataObjectModification<? extends DataObject> getRootNode();
+    @Nonnull DataObjectModification<T> getRootNode();
 
 }
index bc60bdcba2cb3ac350222c5f29aeee2e10ec036f..35b2ce42b662b26783db889419e93f1aa7e0183f 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
 
 /**
@@ -21,10 +22,10 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
  * to their Binding equivalent.
  *
  */
-final class BindingDOMDataTreeChangeListenerAdapter implements DOMDataTreeChangeListener {
+final class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
 
     private final BindingToNormalizedNodeCodec codec;
-    private final DataTreeChangeListener listener;
+    private final DataTreeChangeListener<T> listener;
     private final LogicalDatastoreType store;
 
     BindingDOMDataTreeChangeListenerAdapter(final BindingToNormalizedNodeCodec codec, final DataTreeChangeListener listener,
@@ -36,7 +37,7 @@ final class BindingDOMDataTreeChangeListenerAdapter implements DOMDataTreeChange
 
     @Override
     public void onDataTreeChanged(final Collection<DataTreeCandidate> domChanges) {
-        final Collection<DataTreeModification> bindingChanges = LazyDataTreeModification.from(codec, domChanges, store);
+        final Collection<DataTreeModification<T>> bindingChanges = LazyDataTreeModification.from(codec, domChanges, store);
         listener.onDataTreeChanged(bindingChanges);
     }
 }
index 04c6ad5702be1b765f7c1fbc2515b831d33bad98..ad0ab54e9f700479200f5411316b2430c6908788 100644 (file)
@@ -14,6 +14,7 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 
 
@@ -44,15 +45,15 @@ final class BindingDOMDataTreeChangeServiceAdapter implements DataTreeChangeServ
     }
 
     @Override
-    public <L extends DataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(
-            final DataTreeIdentifier treeId, final L listener) {
+    public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(
+            final DataTreeIdentifier<T> treeId, final L listener) {
         final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId);
-        final BindingDOMDataTreeChangeListenerAdapter domListener = new BindingDOMDataTreeChangeListenerAdapter(codec,listener, treeId.getDatastoreType());
-        final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter> domReg = dataTreeChangeService.registerDataTreeChangeListener(domIdentifier, domListener);
+        final BindingDOMDataTreeChangeListenerAdapter<T> domListener = new BindingDOMDataTreeChangeListenerAdapter<>(codec,listener, treeId.getDatastoreType());
+        final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter<T>> domReg = dataTreeChangeService.registerDataTreeChangeListener(domIdentifier, domListener);
         return new BindingDataTreeChangeListenerRegistration<>(listener,domReg);
     }
 
-    private DOMDataTreeIdentifier toDomTreeIdentifier(final DataTreeIdentifier treeId) {
+    private DOMDataTreeIdentifier toDomTreeIdentifier(final DataTreeIdentifier<?> treeId) {
         final YangInstanceIdentifier domPath = codec.toYangInstanceIdentifier(treeId.getRootIdentifier());
         return new DOMDataTreeIdentifier(treeId.getDatastoreType(), domPath);
     }
index 524d97c497fb4459f854e05b416094a43f19aab9..8a92e5fc80f1b7d61c8504a06651b39c4a31532b 100644 (file)
@@ -12,12 +12,11 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
-class BindingDataTreeChangeListenerRegistration<L extends DataTreeChangeListener> extends AbstractListenerRegistration<L> {
+class BindingDataTreeChangeListenerRegistration<L extends DataTreeChangeListener<?>> extends AbstractListenerRegistration<L> {
 
-    private final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter> domReg;
+    private final ListenerRegistration<?> domReg;
 
-    BindingDataTreeChangeListenerRegistration(final L listener,
-            final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter> domReg) {
+    BindingDataTreeChangeListenerRegistration(final L listener, final ListenerRegistration<?> domReg) {
         super(listener);
         this.domReg = Preconditions.checkNotNull(domReg);
     }
index b29ed3849bc4749eaee18ce0f41260c93f9455bf..a165242b30f08d29902240d868b24ca84464fde9 100644 (file)
@@ -15,8 +15,11 @@ import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.ChildOf;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -49,7 +52,7 @@ class LazyDataObjectModification<T extends DataObject> implements DataObjectModi
         this.identifier = codec.deserializePathArgument(domData.getIdentifier());
     }
 
-    static DataObjectModification<? extends DataObject> create(final BindingCodecTreeNode<?> codec,
+    static <T extends DataObject> DataObjectModification<T> create(final BindingCodecTreeNode<T> codec,
             final DataTreeCandidateNode domData) {
         return new LazyDataObjectModification<>(codec,domData);
     }
@@ -152,6 +155,7 @@ class LazyDataObjectModification<T extends DataObject> implements DataObjectModi
         return childNodesCache;
     }
 
+    @Override
     public DataObjectModification<? extends DataObject> getModifiedChild(final PathArgument arg) {
         final List<YangInstanceIdentifier.PathArgument> domArgumentList = new ArrayList<>();
         final BindingCodecTreeNode<?> childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
@@ -166,11 +170,26 @@ class LazyDataObjectModification<T extends DataObject> implements DataObjectModi
         return null;
     }
 
+    @Override
+    @SuppressWarnings("unchecked")
+    public <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(
+            final Class<C> listItem, final K listKey) {
+        return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.IdentifiableItem<>(listItem, listKey));
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
-    public <C extends ChildOf<T>> DataObjectModification<C> getModifiedChild(final Class<C> arg) {
+    public <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(final Class<C> arg) {
         return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.Item<>(arg));
     }
 
+    @Override
+    @SuppressWarnings("unchecked")
+    public <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
+            final Class<C> augmentation) {
+        return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.Item<>(augmentation));
+    }
+
     private T deserialize(final Optional<NormalizedNode<?, ?>> dataAfter) {
         if(dataAfter.isPresent()) {
             return codec.deserialize(dataAfter.get());
index 26fd420c0e4ed90435de16f2c5f588328c07eba2..2a90f96646a7733056353f898beca3c08bdf3770 100644 (file)
@@ -28,38 +28,39 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
  * which are directly accessed by user of data object modification.
  *
  */
-class LazyDataTreeModification implements DataTreeModification {
+class LazyDataTreeModification<T extends DataObject> implements DataTreeModification<T> {
 
-    private final DataTreeIdentifier path;
-    private final DataObjectModification<?> rootNode;
+    private final DataTreeIdentifier<T> path;
+    private final DataObjectModification<T> rootNode;
 
-    LazyDataTreeModification(final LogicalDatastoreType datastoreType, final InstanceIdentifier<?> path, final BindingCodecTreeNode<?> codec, final DataTreeCandidate domChange) {
-        this.path = new DataTreeIdentifier(datastoreType, path);
+    LazyDataTreeModification(final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> path, final BindingCodecTreeNode<T> codec, final DataTreeCandidate domChange) {
+        this.path = new DataTreeIdentifier<>(datastoreType, path);
         this.rootNode = LazyDataObjectModification.create(codec, domChange.getRootNode());
     }
 
     @Override
-    public DataObjectModification<? extends DataObject> getRootNode() {
+    public DataObjectModification<T> getRootNode() {
         return rootNode;
     }
 
     @Override
-    public DataTreeIdentifier getRootPath() {
+    public DataTreeIdentifier<T> getRootPath() {
         return path;
     }
 
-    static DataTreeModification create(final BindingToNormalizedNodeCodec codec, final DataTreeCandidate domChange,
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    static <T extends DataObject> DataTreeModification<T> create(final BindingToNormalizedNodeCodec codec, final DataTreeCandidate domChange,
             final LogicalDatastoreType datastoreType) {
         final Entry<InstanceIdentifier<?>, BindingCodecTreeNode<?>> codecCtx =
                 codec.getSubtreeCodec(domChange.getRootPath());
-        return new LazyDataTreeModification(datastoreType, codecCtx.getKey(), codecCtx.getValue(), domChange);
+        return (DataTreeModification<T>) new LazyDataTreeModification(datastoreType, codecCtx.getKey(), codecCtx.getValue(), domChange);
     }
 
-    static Collection<DataTreeModification> from(final BindingToNormalizedNodeCodec codec,
+    static <T extends DataObject> Collection<DataTreeModification<T>> from(final BindingToNormalizedNodeCodec codec,
             final Collection<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType) {
-        final List<DataTreeModification> result = new ArrayList<>(domChanges.size());
+        final List<DataTreeModification<T>> result = new ArrayList<>(domChanges.size());
         for (final DataTreeCandidate domChange : domChanges) {
-            result.add(create(codec, domChange, datastoreType));
+            result.add(LazyDataTreeModification.<T>create(codec, domChange, datastoreType));
         }
         return result;
     }
index 218233e8f1645f95d62ae78b6c52825abc72728a..888a62813366bbd772ba879d9452f2ad01f55ffc 100644 (file)
@@ -2,6 +2,7 @@ package org.opendaylight.controller.md.sal.binding.impl.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
 import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
 import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_ONE_KEY;
@@ -39,31 +40,32 @@ import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 public class DataTreeChangeListenerTest extends AbstractDataBrokerTest {
 
     private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+    private static final PathArgument TOP_ARGUMENT= TOP_PATH.getPathArguments().iterator().next();
     private static final InstanceIdentifier<TopLevelList> FOO_PATH = path(TOP_FOO_KEY);
     private static final PathArgument FOO_ARGUMENT = Iterables.getLast(FOO_PATH.getPathArguments());
     private static final TopLevelList FOO_DATA = topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY));
     private static final InstanceIdentifier<TopLevelList> BAR_PATH = path(TOP_BAR_KEY);
     private static final PathArgument BAR_ARGUMENT = Iterables.getLast(BAR_PATH.getPathArguments());
     private static final TopLevelList BAR_DATA = topLevelList(TOP_BAR_KEY);
-    private static final DataTreeIdentifier TOP_IDENTIFIER = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
+private static final DataTreeIdentifier<Top> TOP_IDENTIFIER = new DataTreeIdentifier<Top>(LogicalDatastoreType.OPERATIONAL,
             TOP_PATH);
 
     private static final Top TOP_INITIAL_DATA = top(FOO_DATA);
 
     private BindingDOMDataBrokerAdapter dataBrokerImpl;
 
-    private static final class EventCapturingListener implements DataTreeChangeListener {
+    private static final class EventCapturingListener<T extends DataObject> implements DataTreeChangeListener<T> {
 
-        private SettableFuture<Collection<DataTreeModification>> changes = SettableFuture.create();
+        private SettableFuture<Collection<DataTreeModification<T>>> changes = SettableFuture.create();
 
         @Override
-        public void onDataTreeChanged(final Collection<DataTreeModification> changes) {
+        public void onDataTreeChanged(final Collection<DataTreeModification<T>> changes) {
             this.changes.set(changes);
 
         }
 
-        Collection<DataTreeModification> nextEvent() throws Exception {
-            final Collection<DataTreeModification> result = changes.get(200,TimeUnit.MILLISECONDS);
+        Collection<DataTreeModification<T>> nextEvent() throws Exception {
+            final Collection<DataTreeModification<T>> result = changes.get(200,TimeUnit.MILLISECONDS);
             changes = SettableFuture.create();
             return result;
         }
@@ -85,67 +87,61 @@ public class DataTreeChangeListenerTest extends AbstractDataBrokerTest {
 
     @Test
     public void testTopLevelListener() throws Exception {
-        final EventCapturingListener listener = new EventCapturingListener();
+        final EventCapturingListener<Top> listener = new EventCapturingListener<>();
         dataBrokerImpl.registerDataTreeChangeListener(TOP_IDENTIFIER, listener);
 
         createAndVerifyTop(listener);
 
         putTx(BAR_PATH, BAR_DATA).submit().checkedGet();
-        final DataTreeModification afterBarPutEvent = Iterables.getOnlyElement(listener.nextEvent());
-        final DataObjectModification<? extends DataObject> barPutMod =
-                getOnlyChildModification(afterBarPutEvent.getRootNode(), ModificationType.SUBTREE_MODIFIED);
+        final DataObjectModification<Top> afterBarPutEvent = Iterables.getOnlyElement(listener.nextEvent()).getRootNode();
+        verifyModification(afterBarPutEvent, TOP_ARGUMENT, ModificationType.SUBTREE_MODIFIED);
+        final DataObjectModification<TopLevelList> barPutMod = afterBarPutEvent.getModifiedChildListItem(TopLevelList.class, TOP_BAR_KEY);
+        assertNotNull(barPutMod);
         verifyModification(barPutMod, BAR_ARGUMENT, ModificationType.WRITE);
 
         deleteTx(BAR_PATH).submit().checkedGet();
-        final DataTreeModification afterBarDeleteEvent = Iterables.getOnlyElement(listener.nextEvent());
-        final DataObjectModification<? extends DataObject> barDeleteMod =
-                getOnlyChildModification(afterBarDeleteEvent.getRootNode(), ModificationType.SUBTREE_MODIFIED);
+        final DataObjectModification<Top> afterBarDeleteEvent = Iterables.getOnlyElement(listener.nextEvent()).getRootNode();
+        verifyModification(afterBarDeleteEvent, TOP_ARGUMENT, ModificationType.SUBTREE_MODIFIED);
+        final DataObjectModification<TopLevelList> barDeleteMod = afterBarDeleteEvent.getModifiedChildListItem(TopLevelList.class, TOP_BAR_KEY);
         verifyModification(barDeleteMod, BAR_ARGUMENT, ModificationType.DELETE);
     }
 
     @Test
     public void testWildcardedListListener() throws Exception {
-        final EventCapturingListener listener = new EventCapturingListener();
-        final DataTreeIdentifier wildcard = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TOP_PATH.child(TopLevelList.class));
+        final EventCapturingListener<TopLevelList> listener = new EventCapturingListener<>();
+        final DataTreeIdentifier<TopLevelList> wildcard = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, TOP_PATH.child(TopLevelList.class));
         dataBrokerImpl.registerDataTreeChangeListener(wildcard, listener);
 
         putTx(TOP_PATH, TOP_INITIAL_DATA).submit().checkedGet();
 
-        final DataTreeModification fooWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
+        final DataTreeModification<TopLevelList> fooWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
         assertEquals(FOO_PATH, fooWriteEvent.getRootPath().getRootIdentifier());
         verifyModification(fooWriteEvent.getRootNode(), FOO_ARGUMENT, ModificationType.WRITE);
 
         putTx(BAR_PATH, BAR_DATA).submit().checkedGet();
-        final DataTreeModification barWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
+        final DataTreeModification<TopLevelList> barWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
         assertEquals(BAR_PATH, barWriteEvent.getRootPath().getRootIdentifier());
         verifyModification(barWriteEvent.getRootNode(), BAR_ARGUMENT, ModificationType.WRITE);
 
         deleteTx(BAR_PATH).submit().checkedGet();
-        final DataTreeModification barDeleteEvent = Iterables.getOnlyElement(listener.nextEvent());
+        final DataTreeModification<TopLevelList> barDeleteEvent = Iterables.getOnlyElement(listener.nextEvent());
         assertEquals(BAR_PATH, barDeleteEvent.getRootPath().getRootIdentifier());
         verifyModification(barDeleteEvent.getRootNode(), BAR_ARGUMENT, ModificationType.DELETE);
     }
 
 
 
-    private void createAndVerifyTop(final EventCapturingListener listener) throws Exception {
+    private void createAndVerifyTop(final EventCapturingListener<Top> listener) throws Exception {
         putTx(TOP_PATH,TOP_INITIAL_DATA).submit().checkedGet();
-        final Collection<DataTreeModification> events = listener.nextEvent();
+        final Collection<DataTreeModification<Top>> events = listener.nextEvent();
 
         assertFalse("Non empty collection should be received.",events.isEmpty());
-        final DataTreeModification initialWrite = Iterables.getOnlyElement(events);
+        final DataTreeModification<Top> initialWrite = Iterables.getOnlyElement(events);
         final DataObjectModification<? extends DataObject> initialNode = initialWrite.getRootNode();
         verifyModification(initialNode,TOP_PATH.getPathArguments().iterator().next(),ModificationType.WRITE);
         assertEquals(TOP_INITIAL_DATA, initialNode.getDataAfter());
     }
 
-    private DataObjectModification<? extends DataObject> getOnlyChildModification(final DataObjectModification<? extends DataObject> parentMod,final DataObjectModification.ModificationType eventType) throws Exception {
-        assertEquals(eventType,parentMod.getModificationType());
-        final Collection<DataObjectModification<? extends DataObject>> childMod = parentMod.getModifiedChildren();
-        assertFalse("Non empty children modification",childMod.isEmpty());
-        return Iterables.getOnlyElement(childMod);
-    }
-
     private void verifyModification(final DataObjectModification<? extends DataObject> barWrite, final PathArgument pathArg,
             final ModificationType eventType) {
         assertEquals(pathArg.getType(), barWrite.getDataType());