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> + 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 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, K extends Identifier> DataObjectModification getModifiedChildListItem( - final Class listItem, final K listKey) { - return (DataObjectModification) getModifiedChild(new InstanceIdentifier.IdentifiableItem<>(listItem, listKey)); + public & ChildOf, 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, + 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> 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); } }