From 3a9a552879aeb9760dcce0ade0903c74ed864820 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Wed, 18 Mar 2020 11:27:54 +0100 Subject: [PATCH] Add DataTreeCandidateInputOutput DataTreeCandidate is a yangtools concept, which is commonly serialized by users of DataTree. Make sure we support its serialization in the binary stream -- importing the implementation from controller as of e66759266dc43d5f58b2837aca5047b42c205e4a. Change-Id: I5d63a3f9b1ced38e762ce0a74fb3fd820d67614a Signed-off-by: Robert Varga (cherry picked from commit 5cdad3bad8fe5f0049c157c4e24ae94db1be7b6d) --- .../binfmt/AbstractDataTreeCandidateNode.java | 66 ++++++ .../binfmt/DataTreeCandidateInputOutput.java | 215 ++++++++++++++++++ .../binfmt/DeletedDataTreeCandidateNode.java | 54 +++++ .../binfmt/ModifiedDataTreeCandidateNode.java | 60 +++++ 4 files changed, 395 insertions(+) create mode 100644 yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractDataTreeCandidateNode.java create mode 100644 yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DataTreeCandidateInputOutput.java create mode 100644 yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DeletedDataTreeCandidateNode.java create mode 100644 yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ModifiedDataTreeCandidateNode.java diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractDataTreeCandidateNode.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractDataTreeCandidateNode.java new file mode 100644 index 0000000000..7d4a8769f2 --- /dev/null +++ b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractDataTreeCandidateNode.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.yangtools.yang.data.codec.binfmt; + +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.Optional; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +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; + +/** + * Abstract base class for our internal implementation of {@link DataTreeCandidateNode}, + * which we instantiate from a serialized stream. We do not retain the before-image and + * do not implement {@link #getModifiedChild(PathArgument)}, as that method is only + * useful for end users. Instances based on this class should never be leaked outside of + * this component. + */ +abstract class AbstractDataTreeCandidateNode implements DataTreeCandidateNode { + private final ModificationType type; + + protected AbstractDataTreeCandidateNode(final ModificationType type) { + this.type = requireNonNull(type); + } + + @Override + public final Optional getModifiedChild(final PathArgument identifier) { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public final ModificationType getModificationType() { + return type; + } + + @Override + public final Optional> getDataBefore() { + throw new UnsupportedOperationException("Before-image not available after serialization"); + } + + static DataTreeCandidateNode createUnmodified() { + return new AbstractDataTreeCandidateNode(ModificationType.UNMODIFIED) { + @Override + public PathArgument getIdentifier() { + throw new UnsupportedOperationException("Root node does not have an identifier"); + } + + @Override + public Optional> getDataAfter() { + throw new UnsupportedOperationException("After-image not available after serialization"); + } + + @Override + public Collection getChildNodes() { + throw new UnsupportedOperationException("Children not available after serialization"); + } + }; + } +} diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DataTreeCandidateInputOutput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DataTreeCandidateInputOutput.java new file mode 100644 index 0000000000..3d23fc5c6c --- /dev/null +++ b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DataTreeCandidateInputOutput.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.yangtools.yang.data.codec.binfmt; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import org.eclipse.jdt.annotation.NonNull; +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.stream.ReusableStreamReceiver; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNodes; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates; +import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; +import org.opendaylight.yangtools.yang.data.impl.schema.ReusableImmutableNormalizedNodeStreamWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility serialization/deserialization for {@link DataTreeCandidate}. Note that this utility does not maintain + * before-image information across serialization. + * + * @author Robert Varga + */ +@Beta +public final class DataTreeCandidateInputOutput { + private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidateInputOutput.class); + private static final byte DELETE = 0; + private static final byte SUBTREE_MODIFIED = 1; + private static final byte UNMODIFIED = 2; + private static final byte WRITE = 3; + private static final byte APPEARED = 4; + private static final byte DISAPPEARED = 5; + + private DataTreeCandidateInputOutput() { + + } + + public static @NonNull DataTreeCandidate readDataTreeCandidate(final NormalizedNodeDataInput in) + throws IOException { + return readDataTreeCandidate(in, ReusableImmutableNormalizedNodeStreamWriter.create()); + } + + public static @NonNull DataTreeCandidate readDataTreeCandidate(final NormalizedNodeDataInput in, + final ReusableStreamReceiver receiver) throws IOException { + final YangInstanceIdentifier rootPath = in.readYangInstanceIdentifier(); + final byte type = in.readByte(); + + final DataTreeCandidateNode rootNode; + switch (type) { + case APPEARED: + rootNode = ModifiedDataTreeCandidateNode.create(ModificationType.APPEARED, readChildren(in, receiver)); + break; + case DELETE: + rootNode = DeletedDataTreeCandidateNode.create(); + break; + case DISAPPEARED: + rootNode = ModifiedDataTreeCandidateNode.create(ModificationType.DISAPPEARED, + readChildren(in, receiver)); + break; + case SUBTREE_MODIFIED: + rootNode = ModifiedDataTreeCandidateNode.create(ModificationType.SUBTREE_MODIFIED, + readChildren(in, receiver)); + break; + case WRITE: + rootNode = DataTreeCandidateNodes.written(in.readNormalizedNode(receiver)); + break; + case UNMODIFIED: + rootNode = AbstractDataTreeCandidateNode.createUnmodified(); + break; + default: + throw new IllegalArgumentException("Unhandled node type " + type); + } + + return DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode); + } + + public static void writeDataTreeCandidate(final NormalizedNodeDataOutput out, final DataTreeCandidate candidate) + throws IOException { + out.writeYangInstanceIdentifier(candidate.getRootPath()); + + final DataTreeCandidateNode node = candidate.getRootNode(); + switch (node.getModificationType()) { + case APPEARED: + out.writeByte(APPEARED); + writeChildren(out, node.getChildNodes()); + break; + case DELETE: + out.writeByte(DELETE); + break; + case DISAPPEARED: + out.writeByte(DISAPPEARED); + writeChildren(out, node.getChildNodes()); + break; + case SUBTREE_MODIFIED: + out.writeByte(SUBTREE_MODIFIED); + writeChildren(out, node.getChildNodes()); + break; + case UNMODIFIED: + out.writeByte(UNMODIFIED); + break; + case WRITE: + out.writeByte(WRITE); + out.writeNormalizedNode(node.getDataAfter().get()); + break; + default: + throwUnhandledNodeType(node); + } + } + + private static DataTreeCandidateNode readModifiedNode(final ModificationType type, final NormalizedNodeDataInput in, + final ReusableStreamReceiver receiver) throws IOException { + final PathArgument identifier = in.readPathArgument(); + final Collection children = readChildren(in, receiver); + if (children.isEmpty()) { + LOG.debug("Modified node {} does not have any children, not instantiating it", identifier); + return null; + } + + return ModifiedDataTreeCandidateNode.create(identifier, type, children); + } + + private static Collection readChildren(final NormalizedNodeDataInput in, + final ReusableStreamReceiver receiver) throws IOException { + final int size = in.readInt(); + if (size == 0) { + return ImmutableList.of(); + } + + final Collection ret = new ArrayList<>(size); + for (int i = 0; i < size; ++i) { + final DataTreeCandidateNode child = readNode(in, receiver); + if (child != null) { + ret.add(child); + } + } + return ret; + } + + private static DataTreeCandidateNode readNode(final NormalizedNodeDataInput in, + final ReusableStreamReceiver receiver) throws IOException { + final byte type = in.readByte(); + switch (type) { + case APPEARED: + return readModifiedNode(ModificationType.APPEARED, in, receiver); + case DELETE: + return DeletedDataTreeCandidateNode.create(in.readPathArgument()); + case DISAPPEARED: + return readModifiedNode(ModificationType.DISAPPEARED, in, receiver); + case SUBTREE_MODIFIED: + return readModifiedNode(ModificationType.SUBTREE_MODIFIED, in, receiver); + case UNMODIFIED: + return null; + case WRITE: + return DataTreeCandidateNodes.written(in.readNormalizedNode(receiver)); + default: + throw new IllegalArgumentException("Unhandled node type " + type); + } + } + + private static void writeChildren(final NormalizedNodeDataOutput out, + final Collection children) throws IOException { + out.writeInt(children.size()); + for (DataTreeCandidateNode child : children) { + writeNode(out, child); + } + } + + private static void writeNode(final NormalizedNodeDataOutput out, final DataTreeCandidateNode node) + throws IOException { + switch (node.getModificationType()) { + case APPEARED: + out.writeByte(APPEARED); + out.writePathArgument(node.getIdentifier()); + writeChildren(out, node.getChildNodes()); + break; + case DELETE: + out.writeByte(DELETE); + out.writePathArgument(node.getIdentifier()); + break; + case DISAPPEARED: + out.writeByte(DISAPPEARED); + out.writePathArgument(node.getIdentifier()); + writeChildren(out, node.getChildNodes()); + break; + case SUBTREE_MODIFIED: + out.writeByte(SUBTREE_MODIFIED); + out.writePathArgument(node.getIdentifier()); + writeChildren(out, node.getChildNodes()); + break; + case WRITE: + out.writeByte(WRITE); + out.writeNormalizedNode(node.getDataAfter().get()); + break; + case UNMODIFIED: + out.writeByte(UNMODIFIED); + break; + default: + throwUnhandledNodeType(node); + } + } + + private static void throwUnhandledNodeType(final DataTreeCandidateNode node) { + throw new IllegalArgumentException("Unhandled node type " + node.getModificationType()); + } +} diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DeletedDataTreeCandidateNode.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DeletedDataTreeCandidateNode.java new file mode 100644 index 0000000000..c3e6b9b570 --- /dev/null +++ b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/DeletedDataTreeCandidateNode.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.yangtools.yang.data.codec.binfmt; + +import java.util.Collection; +import java.util.Optional; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +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; + +/** + * A deserialized {@link DataTreeCandidateNode} which represents a deletion. + */ +abstract class DeletedDataTreeCandidateNode extends AbstractDataTreeCandidateNode { + private DeletedDataTreeCandidateNode() { + super(ModificationType.DELETE); + } + + static DataTreeCandidateNode create() { + return new DeletedDataTreeCandidateNode() { + @Override + public PathArgument getIdentifier() { + throw new UnsupportedOperationException("Root node does not have an identifier"); + } + }; + } + + static DataTreeCandidateNode create(final PathArgument identifier) { + return new DeletedDataTreeCandidateNode() { + @Override + public PathArgument getIdentifier() { + return identifier; + } + }; + } + + @Override + public final Optional> getDataAfter() { + return Optional.empty(); + } + + @Override + public final Collection getChildNodes() { + // We would require the before-image to reconstruct the list of nodes which + // were deleted. + throw new UnsupportedOperationException("Children not available after serialization"); + } +} diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ModifiedDataTreeCandidateNode.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ModifiedDataTreeCandidateNode.java new file mode 100644 index 0000000000..4b156f22af --- /dev/null +++ b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ModifiedDataTreeCandidateNode.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.yangtools.yang.data.codec.binfmt; + +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.Optional; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +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; + +/** + * A deserialized {@link DataTreeCandidateNode} which represents a modification in + * one of its children. + */ +abstract class ModifiedDataTreeCandidateNode extends AbstractDataTreeCandidateNode { + private final Collection children; + + private ModifiedDataTreeCandidateNode(final ModificationType type, + final Collection children) { + super(type); + this.children = requireNonNull(children); + } + + static DataTreeCandidateNode create(final ModificationType type, final Collection children) { + return new ModifiedDataTreeCandidateNode(type, children) { + @Override + public PathArgument getIdentifier() { + throw new UnsupportedOperationException("Root node does not have an identifier"); + } + }; + } + + static DataTreeCandidateNode create(final PathArgument identifier, final ModificationType type, + final Collection children) { + return new ModifiedDataTreeCandidateNode(type, children) { + @Override + public PathArgument getIdentifier() { + return identifier; + } + }; + } + + @Override + public final Optional> getDataAfter() { + throw new UnsupportedOperationException("After-image not available after serialization"); + } + + @Override + public final Collection getChildNodes() { + return children; + } +} -- 2.36.6