X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-dom-adapter%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fadapter%2FLazyDataObjectModification.java;h=29cb1cdf2c1c1fcff76568ca639c36c8fd63091a;hb=11408d627adca7eb71ac956c3ad01f75b6b91596;hp=d0d30068d9d5419ea9cb25d695e2f07c7836513f;hpb=b270139f1ba3e72db7953c62dd374aca9699fcb0;p=mdsal.git
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java
index d0d30068d9..29cb1cdf2c 100644
--- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java
+++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java
@@ -7,31 +7,40 @@
*/
package org.opendaylight.mdsal.binding.dom.adapter;
-import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
+import com.google.common.base.MoreObjects;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
-import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
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.IdentifiableItem;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
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.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Lazily translated {@link DataObjectModification} based on {@link DataTreeCandidateNode}.
*
+ *
* {@link LazyDataObjectModification} 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 data object modification.
@@ -40,58 +49,67 @@ import org.slf4j.LoggerFactory;
*/
final class LazyDataObjectModification implements DataObjectModification {
- private final static Logger LOG = LoggerFactory.getLogger(LazyDataObjectModification.class);
+ private static final Logger LOG = LoggerFactory.getLogger(LazyDataObjectModification.class);
- private final BindingCodecTreeNode codec;
+ private final BindingDataObjectCodecTreeNode codec;
private final DataTreeCandidateNode domData;
private final PathArgument identifier;
- private Collection> childNodesCache;
- private LazyDataObjectModification(final BindingCodecTreeNode codec, final DataTreeCandidateNode domData) {
- this.codec = Preconditions.checkNotNull(codec);
- this.domData = Preconditions.checkNotNull(domData);
+ private volatile Collection> childNodesCache;
+ private volatile ModificationType modificationType;
+
+ private LazyDataObjectModification(final BindingDataObjectCodecTreeNode codec,
+ final DataTreeCandidateNode domData) {
+ this.codec = requireNonNull(codec);
+ this.domData = requireNonNull(domData);
this.identifier = codec.deserializePathArgument(domData.getIdentifier());
}
- static DataObjectModification create(final BindingCodecTreeNode codec,
+ static LazyDataObjectModification create(final BindingDataObjectCodecTreeNode codec,
final DataTreeCandidateNode domData) {
- return new LazyDataObjectModification<>(codec,domData);
+ return new LazyDataObjectModification<>(codec, domData);
}
- private static Collection> from(final BindingCodecTreeNode> parentCodec,
+ private static Collection> from(
+ final BindingDataObjectCodecTreeNode> parentCodec,
final Collection domChildNodes) {
- final List> result = new ArrayList<>(domChildNodes.size());
+ final List> result = new ArrayList<>(domChildNodes.size());
populateList(result, parentCodec, domChildNodes);
return result;
}
- private static void populateList(final List> result,
- final BindingCodecTreeNode> parentCodec, final Collection domChildNodes) {
+ private static void populateList(final List> result,
+ final BindingDataObjectCodecTreeNode> parentCodec,
+ final Collection 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 BindingCodecTreeNode> 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);
+ if (domChildNode.getModificationType() != UNMODIFIED) {
+ 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 BindingCodecTreeNode childCodec = parentCodec.yangPathArgumentChild(
+ domChildNode.getIdentifier());
+ verify(childCodec instanceof BindingDataObjectCodecTreeNode, "Unhandled codec %s for type %s",
+ childCodec, type);
+ populateList(result, type, (BindingDataObjectCodecTreeNode>) 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> result,
- final BindingStructuralType type, final BindingCodecTreeNode> childCodec,
+ private static void populateList(final List> result,
+ final BindingStructuralType type, final BindingDataObjectCodecTreeNode> childCodec,
final DataTreeCandidateNode domChildNode) {
switch (type) {
case INVISIBLE_LIST:
@@ -104,15 +122,17 @@ final class LazyDataObjectModification implements DataObje
case UNKNOWN:
case VISIBLE_CONTAINER:
result.add(create(childCodec, domChildNode));
- default:
break;
+ default:
}
}
- private static void populateListWithSingleCodec(final List> result,
- final BindingCodecTreeNode> codec, final Collection childNodes) {
+ private static void populateListWithSingleCodec(final List> result,
+ final BindingDataObjectCodecTreeNode> codec, final Collection childNodes) {
for (final DataTreeCandidateNode child : childNodes) {
- result.add(create(codec, child));
+ if (child.getModificationType() != UNMODIFIED) {
+ result.add(create(codec, child));
+ }
}
}
@@ -137,68 +157,146 @@ final class LazyDataObjectModification implements DataObje
}
@Override
- public DataObjectModification.ModificationType getModificationType() {
- switch(domData.getModificationType()) {
+ public ModificationType getModificationType() {
+ ModificationType localType = modificationType;
+ if (localType != null) {
+ return localType;
+ }
+
+ switch (domData.getModificationType()) {
+ case APPEARED:
case WRITE:
- return DataObjectModification.ModificationType.WRITE;
- case SUBTREE_MODIFIED:
- return DataObjectModification.ModificationType.SUBTREE_MODIFIED;
+ localType = ModificationType.WRITE;
+ break;
+ case DISAPPEARED:
case DELETE:
- return DataObjectModification.ModificationType.DELETE;
-
+ localType = ModificationType.DELETE;
+ break;
+ case SUBTREE_MODIFIED:
+ localType = resolveSubtreeModificationType();
+ break;
default:
// TODO: Should we lie about modification type instead of exception?
throw new IllegalStateException("Unsupported DOM Modification type " + domData.getModificationType());
}
+
+ modificationType = localType;
+ return localType;
+ }
+
+ private ModificationType resolveSubtreeModificationType() {
+ switch (codec.getChildAddressabilitySummary()) {
+ case ADDRESSABLE:
+ // All children are addressable, it is safe to report SUBTREE_MODIFIED
+ return ModificationType.SUBTREE_MODIFIED;
+ case UNADDRESSABLE:
+ // All children are non-addressable, report WRITE
+ return ModificationType.WRITE;
+ case MIXED:
+ // This case is not completely trivial, as we may have NOT_ADDRESSABLE nodes underneath us. If that
+ // is the case, we need to turn this modification into a WRITE operation, so that the user is able
+ // to observe those nodes being introduced. This is not efficient, but unfortunately unavoidable,
+ // as we cannot accurately represent such changes.
+ for (DataTreeCandidateNode child : domData.getChildNodes()) {
+ if (BindingStructuralType.recursiveFrom(child) == BindingStructuralType.NOT_ADDRESSABLE) {
+ // We have a non-addressable child, turn this modification into a write
+ return ModificationType.WRITE;
+ }
+ }
+
+ // No unaddressable children found, proceed in addressed mode
+ return ModificationType.SUBTREE_MODIFIED;
+ default:
+ throw new IllegalStateException("Unsupported child addressability summary "
+ + codec.getChildAddressabilitySummary());
+ }
}
@Override
- public Collection> getModifiedChildren() {
- if (childNodesCache == null) {
- childNodesCache = from(codec, domData.getChildNodes());
+ public Collection> getModifiedChildren() {
+ Collection> local = childNodesCache;
+ if (local == null) {
+ childNodesCache = local = from(codec, domData.getChildNodes());
}
- return childNodesCache;
+ return local;
+ }
+
+ @Override
+ public > Collection>
+ getModifiedChildren(final Class childType) {
+ return streamModifiedChildren(childType).collect(Collectors.toList());
+ }
+
+ @Override
+ public & DataObject, C extends ChildOf super H>>
+ Collection> getModifiedChildren(final Class caseType,
+ final Class childType) {
+ return streamModifiedChildren(childType)
+ .filter(child -> caseType.equals(child.identifier.getCaseType().orElse(null)))
+ .collect(Collectors.toList());
+ }
+
+ @SuppressWarnings("unchecked")
+ private Stream> streamModifiedChildren(
+ final Class childType) {
+ return getModifiedChildren().stream()
+ .filter(child -> childType.isAssignableFrom(child.getDataType()))
+ .map(child -> (LazyDataObjectModification) child);
}
@Override
public DataObjectModification extends DataObject> getModifiedChild(final PathArgument arg) {
final List domArgumentList = new ArrayList<>();
- final BindingCodecTreeNode> childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
+ final BindingDataObjectCodecTreeNode> childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
final Iterator toEnter = domArgumentList.iterator();
DataTreeCandidateNode current = domData;
while (toEnter.hasNext() && current != null) {
- current = current.getModifiedChild(toEnter.next());
- }
- if (current != null) {
- return create(childCodec, current);
+ current = current.getModifiedChild(toEnter.next()).orElse(null);
}
- return null;
+ return current != null && current.getModificationType() != UNMODIFIED ? create(childCodec, current) : null;
}
@Override
@SuppressWarnings("unchecked")
- public & ChildOf super T>, K extends Identifier> DataObjectModification getModifiedChildListItem(
- final Class listItem, final K listKey) {
- return (DataObjectModification) getModifiedChild(new InstanceIdentifier.IdentifiableItem<>(listItem, listKey));
+ public & ChildOf super T>, K extends Identifier> DataObjectModification
+ getModifiedChildListItem(final Class listItem, final K listKey) {
+ return (DataObjectModification) getModifiedChild(IdentifiableItem.of(listItem, listKey));
}
@Override
@SuppressWarnings("unchecked")
- public > DataObjectModification getModifiedChildContainer(final Class arg) {
- return (DataObjectModification) getModifiedChild(new InstanceIdentifier.Item<>(arg));
+ public & DataObject, C extends Identifiable & ChildOf super H>,
+ K extends Identifier> DataObjectModification getModifiedChildListItem(final Class caseType,
+ final Class listItem, final K listKey) {
+ return (DataObjectModification) getModifiedChild(IdentifiableItem.of(caseType, listItem, listKey));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public > DataObjectModification getModifiedChildContainer(final Class child) {
+ return (DataObjectModification) getModifiedChild(Item.of(child));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public & DataObject, C extends ChildOf super H>> DataObjectModification
+ getModifiedChildContainer(final Class caseType, final Class child) {
+ return (DataObjectModification) getModifiedChild(Item.of(caseType, child));
}
@Override
@SuppressWarnings("unchecked")
public & DataObject> DataObjectModification getModifiedAugmentation(
final Class augmentation) {
- return (DataObjectModification) getModifiedChild(new InstanceIdentifier.Item<>(augmentation));
+ return (DataObjectModification) getModifiedChild(Item.of(augmentation));
}
- private T deserialize(final Optional> dataAfter) {
- if (dataAfter.isPresent()) {
- return codec.deserialize(dataAfter.get());
- }
- return null;
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("identifier", identifier).add("domData", domData).toString();
+ }
+
+ private T deserialize(final Optional dataAfter) {
+ return dataAfter.map(codec::deserialize).orElse(null);
}
}