2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.nb.rfc8040.streams.listeners;
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
13 import java.util.ArrayDeque;
14 import java.util.Collection;
15 import java.util.Deque;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
21 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
24 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
25 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
26 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
27 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
28 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
29 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
30 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 abstract class AbstractWebsocketSerializer<T extends Exception> {
35 private static final Logger LOG = LoggerFactory.getLogger(AbstractWebsocketSerializer.class);
37 private final EffectiveModelContext context;
39 AbstractWebsocketSerializer(final EffectiveModelContext context) {
40 this.context = requireNonNull(context);
43 public final void serialize(final DataTreeCandidate candidate, final boolean leafNodesOnly, final boolean skipData)
46 final Deque<PathArgument> path = new ArrayDeque<>();
47 path.addAll(candidate.getRootPath().getPathArguments());
48 serializeLeafNodesOnly(path, candidate.getRootNode(), skipData);
50 serializeData(candidate.getRootPath().getPathArguments(), candidate.getRootNode(), skipData);
54 final void serializeLeafNodesOnly(final Deque<PathArgument> path, final DataTreeCandidateNode candidate,
55 final boolean skipData) throws T {
56 NormalizedNode node = null;
57 switch (candidate.getModificationType()) {
59 // no reason to do anything with an unmodified node
60 LOG.debug("DataTreeCandidate for a notification is unmodified, not serializing leaves. Candidate: {}",
63 case SUBTREE_MODIFIED:
66 node = candidate.getDataAfter().get();
70 node = candidate.getDataBefore().get();
73 LOG.error("DataTreeCandidate modification has unknown type: {}", candidate.getModificationType());
80 if (node instanceof LeafNode<?> || node instanceof LeafSetNode) {
81 serializeData(path, candidate, skipData);
85 for (DataTreeCandidateNode childNode : candidate.getChildNodes()) {
86 path.add(childNode.getIdentifier());
87 serializeLeafNodesOnly(path, childNode, skipData);
92 private void serializeData(final Collection<PathArgument> dataPath, final DataTreeCandidateNode candidate,
93 final boolean skipData) throws T {
94 var stack = SchemaInferenceStack.of(context);
95 var current = DataSchemaContextTree.from(context).getRoot();
96 for (var arg : dataPath) {
97 final var next = verifyNotNull(current.enterChild(stack, arg),
98 "Failed to resolve %s: cannot find %s in %s", dataPath, arg, current);
102 // Exit to parent if needed
103 if (!stack.isEmpty()) {
107 serializeData(stack.toInference(), dataPath, candidate, skipData);
110 abstract void serializeData(Inference parent, Collection<PathArgument> dataPath, DataTreeCandidateNode candidate,
111 boolean skipData) throws T;
113 abstract void serializePath(Collection<PathArgument> pathArguments) throws T;
115 abstract void serializeOperation(DataTreeCandidateNode candidate) throws T;
117 static final String convertPath(final Collection<PathArgument> path) {
118 final StringBuilder pathBuilder = new StringBuilder();
120 for (PathArgument pathArgument : path) {
121 if (pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier) {
124 pathBuilder.append('/');
125 pathBuilder.append(pathArgument.getNodeType().getNamespace().toString().replace(':', '-'));
126 pathBuilder.append(':');
127 pathBuilder.append(pathArgument.getNodeType().getLocalName());
129 if (pathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
130 pathBuilder.append("[");
131 final Set<Map.Entry<QName, Object>> keys =
132 ((YangInstanceIdentifier.NodeIdentifierWithPredicates) pathArgument).entrySet();
133 for (Map.Entry<QName, Object> key : keys) {
134 pathBuilder.append(key.getKey().getNamespace().toString().replace(':', '-'));
135 pathBuilder.append(':');
136 pathBuilder.append(key.getKey().getLocalName());
137 pathBuilder.append("='");
138 pathBuilder.append(key.getValue().toString());
139 pathBuilder.append('\'');
141 pathBuilder.append(']');
145 return pathBuilder.toString();
148 static final String modificationTypeToOperation(final DataTreeCandidateNode candidate,
149 final ModificationType modificationType) {
150 switch (modificationType) {
152 // shouldn't ever happen since the root of a modification is only triggered by some event
153 LOG.warn("DataTreeCandidate for a notification is unmodified. Candidate: {}", candidate);
155 case SUBTREE_MODIFIED:
158 if (candidate.getDataBefore().isPresent()) {
167 LOG.error("DataTreeCandidate modification has unknown type: {}", candidate.getModificationType());
168 throw new IllegalStateException("Unknown modification type");