From e3c31cee916480b2d9d169c1f5afb1c42efaabe1 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Fri, 29 Jul 2016 16:27:12 +0200 Subject: HONEYCOMB-130: Rename infra packages(remove vpp/v3po) Change-Id: Ic5b90e397e3743623d01b206bc60bc5c7df6b981 Signed-off-by: Maros Marsalek --- .../fd/honeycomb/data/impl/ModificationDiff.java | 278 +++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java (limited to 'infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java') diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java new file mode 100644 index 000000000..710f8a053 --- /dev/null +++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableMap; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +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.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.MixinNode; +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.api.schema.tree.ModificationType; + +/** + * Recursively collects and provides all unique and non-null modifications (modified normalized nodes). + */ +final class ModificationDiff { + + private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap()); + private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE); + + private final Map updates; + + private ModificationDiff(@Nonnull Map updates) { + this.updates = updates; + } + + /** + * Get processed modifications. + * + * @return mapped modifications, where key is keyed {@link YangInstanceIdentifier}. + */ + Map getUpdates() { + return updates; + } + + private ModificationDiff merge(final ModificationDiff other) { + if (this == EMPTY_DIFF) { + return other; + } + + if (other == EMPTY_DIFF) { + return this; + } + + return new ModificationDiff(join(updates, other.updates)); + } + + private static Map join(Map first, + Map second) { + final Map merged = new HashMap<>(); + merged.putAll(first); + merged.putAll(second); + return merged; + } + + private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) { + return new ModificationDiff(ImmutableMap.of(id, NormalizedNodeUpdate.create(id, candidate))); + } + + /** + * Produce an aggregated diff from a candidate node recursively. MixinNodes are ignored as modifications and so + * are complex nodes which direct leaves were not modified. + */ + @Nonnull + static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid, + @Nonnull final DataTreeCandidateNode currentCandidate) { + // recursively process child nodes for exact modifications + return recursivelyChildrenFromCandidate(yangIid, currentCandidate) + // also add modification on current level, if elligible + .merge(isModification(currentCandidate) + ? ModificationDiff.create(yangIid, currentCandidate) + : EMPTY_DIFF); + } + + /** + * Check whether current node was modified. {@link MixinNode}s are ignored + * and only nodes which direct leaves(or choices) are modified are considered a modification. + */ + private static Boolean isModification(@Nonnull final DataTreeCandidateNode currentCandidate) { + // Mixin nodes are not considered modifications + if (isMixin(currentCandidate) && !isAugment(currentCandidate)) { + return false; + } else { + return isCurrentModified(currentCandidate); + } + } + + private static Boolean isCurrentModified(final @Nonnull DataTreeCandidateNode currentCandidate) { + // Check if there are any modified leaves and if so, consider current node as modified + final Boolean directLeavesModified = currentCandidate.getChildNodes().stream() + .filter(ModificationDiff::isLeaf) + // For some reason, we get modifications on unmodified list keys TODO debug and report ODL bug + // and that messes up our modifications collection here, so we need to skip + .filter(ModificationDiff::isBeforeAndAfterDifferent) + .filter(child -> LEAF_MODIFICATIONS.contains(child.getModificationType())) + .findFirst() + .isPresent(); + + return directLeavesModified + // Also check choices (choices do not exist in BA world and if anything within a choice was modified, + // consider its parent as being modified) + || currentCandidate.getChildNodes().stream() + .filter(ModificationDiff::isChoice) + // Recursively check each choice if there was any change to it + .filter(ModificationDiff::isCurrentModified) + .findFirst() + .isPresent(); + } + + /** + * Process all non-leaf child nodes recursively, creating aggregated {@link ModificationDiff}. + */ + private static ModificationDiff recursivelyChildrenFromCandidate(final @Nonnull YangInstanceIdentifier yangIid, + final @Nonnull DataTreeCandidateNode currentCandidate) { + // recursively process child nodes for specific modifications + return currentCandidate.getChildNodes().stream() + // not interested in modifications to leaves + .filter(child -> !isLeaf(child)) + .map(candidate -> recursivelyFromCandidate(yangIid.node(candidate.getIdentifier()), candidate)) + .reduce(ModificationDiff::merge) + .orElse(EMPTY_DIFF); + } + + /** + * Check whether candidate.before and candidate.after is different. If not return false. + */ + private static boolean isBeforeAndAfterDifferent(@Nonnull final DataTreeCandidateNode candidateNode) { + if (candidateNode.getDataBefore().isPresent()) { + return !candidateNode.getDataBefore().get().equals(candidateNode.getDataAfter().orNull()); + } + + // considering not a modification if data after is also null + return candidateNode.getDataAfter().isPresent(); + } + + /** + * Check whether candidate node is for a leaf type node. + */ + private static boolean isLeaf(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof LeafNode + || candidateNode.getDataBefore().orNull() instanceof LeafNode; + } + + /** + * Check whether candidate node is for a Mixin type node. + */ + private static boolean isMixin(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof MixinNode + || candidateNode.getDataBefore().orNull() instanceof MixinNode; + } + + /** + * Check whether candidate node is for an Augmentation type node. + */ + private static boolean isAugment(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof AugmentationNode + || candidateNode.getDataBefore().orNull() instanceof AugmentationNode; + } + + /** + * Check whether candidate node is for a Choice type node. + */ + private static boolean isChoice(final DataTreeCandidateNode candidateNode) { + // orNull intentional, some candidate nodes have both data after and data before null + return candidateNode.getDataAfter().orNull() instanceof ChoiceNode + || candidateNode.getDataBefore().orNull() instanceof ChoiceNode; + } + + @Override + public String toString() { + return "ModificationDiff{updates=" + updates + '}'; + } + + /** + * Update to a normalized node identifiable by its {@link YangInstanceIdentifier}. + */ + static final class NormalizedNodeUpdate { + + @Nonnull + private final YangInstanceIdentifier id; + @Nullable + private final NormalizedNode dataBefore; + @Nullable + private final NormalizedNode dataAfter; + + private NormalizedNodeUpdate(@Nonnull final YangInstanceIdentifier id, + @Nullable final NormalizedNode dataBefore, + @Nullable final NormalizedNode dataAfter) { + this.id = checkNotNull(id); + this.dataAfter = dataAfter; + this.dataBefore = dataBefore; + } + + @Nullable + public NormalizedNode getDataBefore() { + return dataBefore; + } + + @Nullable + public NormalizedNode getDataAfter() { + return dataAfter; + } + + @Nonnull + public YangInstanceIdentifier getId() { + return id; + } + + static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id, + @Nonnull final DataTreeCandidateNode candidate) { + return create(id, candidate.getDataBefore().orNull(), candidate.getDataAfter().orNull()); + } + + static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id, + @Nullable final NormalizedNode dataBefore, + @Nullable final NormalizedNode dataAfter) { + checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null"); + return new NormalizedNodeUpdate(id, dataBefore, dataAfter); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + + final NormalizedNodeUpdate that = (NormalizedNodeUpdate) other; + + return id.equals(that.id); + + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String toString() { + return "NormalizedNodeUpdate{" + "id=" + id + + ", dataBefore=" + dataBefore + + ", dataAfter=" + dataAfter + + '}'; + } + } + +} -- cgit 1.2.3-korg