--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.codec.modification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+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.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+
+/**
+ * Defines structural mapping of Normalized Node to Binding data addressable by Instance Identifier.
+ *
+ * <p>
+ * Not all binding data are addressable by instance identifier and there are some differences.
+ *
+ * <p>
+ * See {@link #NOT_ADDRESSABLE},{@link #INVISIBLE_CONTAINER},{@link #VISIBLE_CONTAINER} for more details.
+ */
+@Beta
+enum BindingStructuralType {
+
+ /**
+ * DOM Item is not addressable in Binding Instance Identifier, data is not lost, but are available only
+ * via parent object.
+ *
+ * <p>
+ * Such types of data are leaf-lists, leafs, list without keys or anyxml.
+ *
+ */
+ NOT_ADDRESSABLE,
+ /**
+ * Data container is addressable in NormalizedNode format, but in Binding it is not represented in
+ * Instance Identifier.
+ *
+ * <p>
+ * This are choice / case nodes.
+ *
+ * <p>
+ * This data is still accessible using parent object and their children are addressable.
+ *
+ */
+ INVISIBLE_CONTAINER,
+ /**
+ * Data container is addressable in NormalizedNode format, but in Binding it is not represented in
+ * Instance Identifier.
+ *
+ * <p>
+ * This are list nodes.
+ *
+ * <p>
+ * This data is still accessible using parent object and their children are addressable.
+ *
+ */
+ INVISIBLE_LIST,
+ /**
+ * Data container is addressable in Binding Instance Identifier format and also YangInstanceIdentifier
+ * format.
+ *
+ */
+ VISIBLE_CONTAINER,
+ /**
+ * Mapping algorithm was unable to detect type or was not updated after introduction of new NormalizedNode
+ * type.
+ */
+ UNKNOWN;
+
+ static BindingStructuralType from(final DataTreeCandidateNode domChildNode) {
+ final Optional<NormalizedNode<?, ?>> dataBased = domChildNode.getDataAfter().or(domChildNode.getDataBefore());
+ if (dataBased.isPresent()) {
+ return from(dataBased.get());
+ }
+ return from(domChildNode.getIdentifier());
+ }
+
+ private static BindingStructuralType from(final PathArgument identifier) {
+ if (identifier instanceof NodeIdentifierWithPredicates || identifier instanceof AugmentationIdentifier) {
+ return VISIBLE_CONTAINER;
+ }
+ if (identifier instanceof NodeWithValue) {
+ return NOT_ADDRESSABLE;
+ }
+ return UNKNOWN;
+ }
+
+ static BindingStructuralType from(final NormalizedNode<?, ?> data) {
+ if (isNotAddressable(data)) {
+ return NOT_ADDRESSABLE;
+ }
+ if (data instanceof MapNode) {
+ return INVISIBLE_LIST;
+ }
+ if (data instanceof ChoiceNode) {
+ return INVISIBLE_CONTAINER;
+ }
+ if (isVisibleContainer(data)) {
+ return VISIBLE_CONTAINER;
+ }
+ return UNKNOWN;
+ }
+
+ private static boolean isVisibleContainer(final NormalizedNode<?, ?> data) {
+ return data instanceof MapEntryNode || data instanceof ContainerNode || data instanceof AugmentationNode;
+ }
+
+ private static boolean isNotAddressable(final NormalizedNode<?, ?> normalizedNode) {
+ return normalizedNode instanceof LeafNode || normalizedNode instanceof AnyXmlNode
+ || normalizedNode instanceof LeafSetNode || normalizedNode instanceof LeafSetEntryNode;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.codec.modification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.binding.javav2.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.javav2.api.DataTreeModification;
+import org.opendaylight.mdsal.binding.javav2.api.TreeNodeModification;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingTreeNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Lazily translated {@link DataTreeModification} based on {@link DataTreeCandidate}.
+ *
+ * <p>
+ * {@link DataTreeModification} represents Data tree change event, but whole tree is not translated or
+ * resolved eagerly, but only child nodes which are directly accessed by user of tree node modification.
+ *
+ */
+@Beta
+public class LazyDataTreeModification<T extends TreeNode> implements DataTreeModification<T> {
+
+ private final DataTreeIdentifier<T> path;
+ private final TreeNodeModification<T> rootNode;
+
+ private LazyDataTreeModification(final DataTreeIdentifier<T> path, final TreeNodeModification<T> modification) {
+ this.path = Preconditions.checkNotNull(path);
+ this.rootNode = Preconditions.checkNotNull(modification);
+ }
+
+ @Nonnull
+ @Override
+ public TreeNodeModification<T> getRootNode() {
+ return rootNode;
+ }
+
+ @Nonnull
+ @Override
+ public DataTreeIdentifier<T> getRootPath() {
+ return path;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static <T extends TreeNode> DataTreeModification<T> create(final BindingToNormalizedNodeCodec codec,
+ final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType) {
+ final Entry<InstanceIdentifier<?>, BindingTreeNodeCodec<?>> codecCtx =
+ codec.getSubtreeCodec(domChange.getRootPath());
+ final DataTreeIdentifier<?> path = DataTreeIdentifier.create(datastoreType, codecCtx.getKey());
+ final TreeNodeModification<?> modification =
+ LazyTreeNodeModification.create(codecCtx.getValue(), domChange.getRootNode());
+ return new LazyDataTreeModification(path, modification);
+ }
+
+ /**
+ * Create instance of Binding date tree modification according to DOM candidate of changes.
+ *
+ * @param codec
+ * - codec for modificated data
+ * @param candidate
+ * - changted DOM data
+ * @return modificated data tree
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <T extends TreeNode> DataTreeModification<T> create(final BindingToNormalizedNodeCodec codec,
+ final DOMDataTreeCandidate candidate) {
+ final Entry<InstanceIdentifier<?>, BindingTreeNodeCodec<?>> codecCtx =
+ codec.getSubtreeCodec(candidate.getRootPath().getRootIdentifier());
+ final DataTreeIdentifier<?> path =
+ DataTreeIdentifier.create(candidate.getRootPath().getDatastoreType(), codecCtx.getKey());
+ final TreeNodeModification<?> modification =
+ LazyTreeNodeModification.create(codecCtx.getValue(), candidate.getRootNode());
+ return new LazyDataTreeModification(path, modification);
+ }
+
+ /**
+ * DOM data changes to new Binding data.
+ *
+ * @param codec
+ * - Binding to DOM codec
+ * @param domChanges
+ * - DOM data changes
+ * @param datastoreType
+ * - datastore type
+ * @return collection of new Binding data according to DOM data changes
+ */
+ public static <T extends TreeNode> Collection<DataTreeModification<T>> from(
+ final BindingToNormalizedNodeCodec codec, final Collection<DataTreeCandidate> domChanges,
+ final LogicalDatastoreType datastoreType) {
+ final List<DataTreeModification<T>> result = new ArrayList<>(domChanges.size());
+ for (final DataTreeCandidate domChange : domChanges) {
+ result.add(LazyDataTreeModification.create(codec, domChange, datastoreType));
+ }
+ return result;
+ }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.codec.modification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.api.TreeNodeModification;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingTreeNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.base.IdentifiableItem;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Item;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.TreeChildNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lazily translated {@link TreeNodeModification} based on {@link DataTreeCandidateNode}.
+ *
+ * <p>
+ * {@link LazyTreeNodeModification} represents Data tree change event, but whole tree is not translated or
+ * resolved eagerly, but only child nodes which are directly accessed by user of tree node modification.
+ *
+ * @param <T>
+ * Type of Binding Tree Node
+ */
+@Beta
+final class LazyTreeNodeModification<T extends TreeNode> implements TreeNodeModification<T> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LazyTreeNodeModification.class);
+
+ private final BindingTreeNodeCodec<T> codec;
+ private final DataTreeCandidateNode domData;
+ private final TreeArgument<?> identifier;
+ private Collection<TreeNodeModification<? extends TreeNode>> childNodesCache;
+
+ private LazyTreeNodeModification(final BindingTreeNodeCodec<T> codec, final DataTreeCandidateNode domData) {
+ this.codec = Preconditions.checkNotNull(codec);
+ this.domData = Preconditions.checkNotNull(domData);
+ this.identifier = codec.deserializePathArgument(domData.getIdentifier());
+ }
+
+ static <T extends TreeNode> TreeNodeModification<T> create(final BindingTreeNodeCodec<T> codec,
+ final DataTreeCandidateNode domData) {
+ return new LazyTreeNodeModification<>(codec, domData);
+ }
+
+ private static Collection<TreeNodeModification<? extends TreeNode>> from(final BindingTreeNodeCodec<?> parentCodec,
+ final Collection<DataTreeCandidateNode> domChildNodes) {
+ final List<TreeNodeModification<? extends TreeNode>> result = new ArrayList<>(domChildNodes.size());
+ populateList(result, parentCodec, domChildNodes);
+ return result;
+ }
+
+ private static void populateList(final List<TreeNodeModification<? extends TreeNode>> result,
+ final BindingTreeNodeCodec<?> parentCodec, final Collection<DataTreeCandidateNode> domChildNodes) {
+ for (final DataTreeCandidateNode domChildNode : domChildNodes) {
+ final BindingStructuralType type = BindingStructuralType.from(domChildNode);
+ if (type != BindingStructuralType.NOT_ADDRESSABLE) {
+ /*
+ * Even if type is UNKNOWN, from perspective of BindingStructuralType we try to load codec for
+ * it. We will use that type to further specify debug log.
+ */
+ try {
+ final BindingTreeNodeCodec<?> childCodec =
+ parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
+ populateList(result, type, childCodec, domChildNode);
+ } catch (final IllegalArgumentException e) {
+ if (type == BindingStructuralType.UNKNOWN) {
+ LOG.debug("Unable to deserialize unknown DOM node {}", domChildNode, e);
+ } else {
+ LOG.debug("Binding representation for DOM node {} was not found", domChildNode, e);
+ }
+ }
+ }
+ }
+ }
+
+ private static void populateList(final List<TreeNodeModification<? extends TreeNode>> result,
+ final BindingStructuralType type, final BindingTreeNodeCodec<?> childCodec,
+ final DataTreeCandidateNode domChildNode) {
+ switch (type) {
+ case INVISIBLE_LIST:
+ // We use parent codec intentionally.
+ populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
+ break;
+ case INVISIBLE_CONTAINER:
+ populateList(result, childCodec, domChildNode.getChildNodes());
+ break;
+ case UNKNOWN:
+ case VISIBLE_CONTAINER:
+ result.add(create(childCodec, domChildNode));
+ break;
+ default:
+ }
+ }
+
+ private static void populateListWithSingleCodec(final List<TreeNodeModification<? extends TreeNode>> result,
+ final BindingTreeNodeCodec<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
+ for (final DataTreeCandidateNode child : childNodes) {
+ result.add(create(codec, child));
+ }
+ }
+
+ @Nullable
+ @Override
+ public T getDataBefore() {
+ return deserialize(domData.getDataBefore());
+ }
+
+ @Nullable
+ @Override
+ public T getDataAfter() {
+ return deserialize(domData.getDataAfter());
+ }
+
+ @Nonnull
+ @Override
+ public Class<T> getDataType() {
+ return codec.getBindingClass();
+ }
+
+ @Nonnull
+ @Override
+ public TreeArgument<?> getIdentifier() {
+ return identifier;
+ }
+
+ @Nonnull
+ @Override
+ public TreeNodeModification.ModificationType getModificationType() {
+ switch (domData.getModificationType()) {
+ case APPEARED:
+ case WRITE:
+ return TreeNodeModification.ModificationType.WRITE;
+ case SUBTREE_MODIFIED:
+ return TreeNodeModification.ModificationType.SUBTREE_MODIFIED;
+ case DISAPPEARED:
+ case DELETE:
+ return TreeNodeModification.ModificationType.DELETE;
+
+ default:
+ // TODO: Should we lie about modification type instead of exception?
+ throw new IllegalStateException("Unsupported DOM Modification type " + domData.getModificationType());
+ }
+ }
+
+ @Nonnull
+ @Override
+ public Collection<TreeNodeModification<? extends TreeNode>> getModifiedChildren() {
+ if (childNodesCache == null) {
+ childNodesCache = from(codec, domData.getChildNodes());
+ }
+ return childNodesCache;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <C extends TreeChildNode<? super T, ?>> Collection<TreeNodeModification<C>>
+ getModifiedChildren(@Nonnull final Class<C> childType) {
+ final List<TreeNodeModification<C>> children = new ArrayList<>();
+ for (final TreeNodeModification<? extends TreeNode> potential : getModifiedChildren()) {
+ if (childType.isAssignableFrom(potential.getDataType())) {
+ children.add((TreeNodeModification<C>) potential);
+ }
+ }
+ return children;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Nullable
+ @Override
+ public TreeNodeModification<? extends TreeNode> getModifiedChild(final TreeArgument childArgument) {
+ final List<YangInstanceIdentifier.PathArgument> domArgumentList = new ArrayList<>();
+ final BindingTreeNodeCodec<?> childCodec = codec.bindingPathArgumentChild(childArgument, domArgumentList);
+ final Iterator<YangInstanceIdentifier.PathArgument> toEnter = domArgumentList.iterator();
+ DataTreeCandidateNode current = domData;
+ while (toEnter.hasNext() && current != null) {
+ current = current.getModifiedChild(toEnter.next());
+ }
+ if (current != null) {
+ return create(childCodec, current);
+ }
+ return null;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public <C extends IdentifiableItem<T, K> & TreeChildNode<? super T, ?>, K extends IdentifiableItem<T, K>>
+ TreeNodeModification<C>
+ getModifiedChildListItem(@Nonnull final Class<C> listItem, @Nonnull final K listKey) {
+ return (TreeNodeModification) getModifiedChild(new IdentifiableItem(listItem, listKey));
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Nullable
+ @Override
+ public <C extends TreeChildNode<? super T, ?>> TreeNodeModification<C>
+ getModifiedChildContainer(@Nonnull final Class<C> child) {
+ return (TreeNodeModification<C>) getModifiedChild(new Item(child));
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Nullable
+ @Override
+ public <C extends Augmentation<T> & TreeNode> TreeNodeModification<C>
+ getModifiedAugmentation(@Nonnull final Class<C> augmentation) {
+ return (TreeNodeModification<C>) getModifiedChild(new Item(augmentation));
+ }
+
+ private T deserialize(final Optional<NormalizedNode<?, ?>> dataAfter) {
+ if (dataAfter.isPresent()) {
+ return codec.deserialize(dataAfter.get());
+ }
+ return null;
+ }
+}
+
* 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.mdsal.binding.javav2.dom.codec;
+package org.opendaylight.mdsal.binding.javav2.dom.codec.impl;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import org.junit.Test;
import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingNormalizedNodeCodec;
-import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.NonCachingCodec;
public class NonCachingCodecTest {
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.codec.modification;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingTreeCodec;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingTreeNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+
+public class LazyDataTreeModificationTest {
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test
+ public void basicTest() throws Exception {
+ final BindingNormalizedNodeCodecRegistry registry = mock(BindingNormalizedNodeCodecRegistry.class);
+ final BindingToNormalizedNodeCodec codec =
+ new BindingToNormalizedNodeCodec(
+ (GeneratedClassLoadingStrategy) GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(),
+ registry);
+ final DOMDataTreeCandidate domDataTreeCandidate = mock(DOMDataTreeCandidate.class);
+ final DOMDataTreeIdentifier domDataTreeIdentifier =
+ new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
+ doReturn(InstanceIdentifier.create(TreeNode.class)).when(registry).fromYangInstanceIdentifier(any());
+ final BindingTreeCodec bindingCodecTree = mock(BindingTreeCodec.class);
+ final BindingTreeNodeCodec bindingCodecTreeNode = mock(BindingTreeNodeCodec.class);
+ doReturn(bindingCodecTreeNode).when(bindingCodecTree).getSubtreeCodec(any(InstanceIdentifier.class));
+ final PathArgument identifier =
+ new YangInstanceIdentifier.NodeIdentifier(QName.create("test", "2017-06-13", "test-l"));
+
+ doReturn(mock(TreeArgument.class)).when(bindingCodecTreeNode).deserializePathArgument(identifier);
+ doReturn(bindingCodecTree).when(registry).getCodecContext();
+ doReturn(domDataTreeIdentifier).when(domDataTreeCandidate).getRootPath();
+ final DataTreeCandidateNode dataTreeCandidate = mock(DataTreeCandidateNode.class);
+ doReturn(dataTreeCandidate).when(domDataTreeCandidate).getRootNode();
+ doReturn(identifier).when(dataTreeCandidate).getIdentifier();
+ assertNotNull(LazyDataTreeModification.create(codec, domDataTreeCandidate));
+ }
+}