Make ListenerAdapter serialize JSON directly
[netconf.git] / restconf / restconf-common / src / main / java / org / opendaylight / restconf / common / serializer / AbstractWebsocketSerializer.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.common.serializer;
9
10 import java.util.ArrayDeque;
11 import java.util.Collection;
12 import java.util.Deque;
13 import java.util.Map;
14 import java.util.Set;
15 import org.opendaylight.yangtools.yang.common.QName;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
18 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
22 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 public abstract class AbstractWebsocketSerializer<T extends Exception> {
28
29     private static final Logger LOG = LoggerFactory.getLogger(AbstractWebsocketSerializer.class);
30
31     public void serialize(DataTreeCandidate candidate, boolean leafNodesOnly, boolean skipData) throws T {
32         final Deque<PathArgument> path = new ArrayDeque<>();
33         path.addAll(candidate.getRootPath().getPathArguments());
34         if (leafNodesOnly) {
35             serializeLeafNodesOnly(path, candidate.getRootNode(), skipData);
36             return;
37         }
38
39         serializeData(path, candidate.getRootNode(), skipData);
40     }
41
42     void serializeLeafNodesOnly(Deque<PathArgument> path, DataTreeCandidateNode candidate, boolean skipData)
43             throws T {
44         NormalizedNode<?, ?> node = null;
45         switch (candidate.getModificationType()) {
46             case UNMODIFIED:
47                 // no reason to do anything with an unmodified node
48                 LOG.debug("DataTreeCandidate for a notification is unmodified, not serializing leaves. Candidate: {}",
49                         candidate);
50                 break;
51             case SUBTREE_MODIFIED:
52             case WRITE:
53             case APPEARED:
54                 node = candidate.getDataAfter().get();
55                 break;
56             case DELETE:
57             case DISAPPEARED:
58                 node = candidate.getDataBefore().get();
59                 break;
60             default:
61                 LOG.error("DataTreeCandidate modification has unknown type: {}", candidate.getModificationType());
62         }
63
64         if (node == null) {
65             return;
66         }
67
68         if (node instanceof LeafNode<?> || node instanceof LeafSetNode) {
69             serializeData(path, candidate, skipData);
70             return;
71         }
72
73         for (DataTreeCandidateNode childNode : candidate.getChildNodes()) {
74             path.add(childNode.getIdentifier());
75             serializeLeafNodesOnly(path, childNode, skipData);
76             path.removeLast();
77         }
78     }
79
80     abstract void serializeData(Collection<PathArgument> path, DataTreeCandidateNode candidate, boolean skipData)
81             throws T;
82
83     abstract void serializePath(Collection<PathArgument> pathArguments) throws T;
84
85     abstract void serializeOperation(DataTreeCandidateNode candidate) throws T;
86
87     String convertPath(Collection<PathArgument> path) {
88         final StringBuilder pathBuilder = new StringBuilder();
89
90         for (PathArgument pathArgument : path) {
91             pathBuilder.append("/");
92             pathBuilder.append(pathArgument.getNodeType().getNamespace().toString().replaceAll(":", "-"));
93             pathBuilder.append(":");
94             pathBuilder.append(pathArgument.getNodeType().getLocalName());
95
96             if (pathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
97                 pathBuilder.append("[");
98                 final Set<Map.Entry<QName, Object>> keys =
99                         ((YangInstanceIdentifier.NodeIdentifierWithPredicates) pathArgument).entrySet();
100                 for (Map.Entry<QName, Object> key : keys) {
101                     pathBuilder.append(key.getKey().getNamespace().toString().replaceAll(":", "-"));
102                     pathBuilder.append(":");
103                     pathBuilder.append(key.getKey().getLocalName());
104                     pathBuilder.append("='");
105                     pathBuilder.append(key.getValue().toString());
106                     pathBuilder.append("'");
107                 }
108                 pathBuilder.append("]");
109             }
110         }
111
112         return pathBuilder.toString();
113     }
114
115     String modificationTypeToOperation(DataTreeCandidateNode candidate, ModificationType modificationType) {
116         switch (modificationType) {
117             case UNMODIFIED:
118                 // shouldn't ever happen since the root of a modification is only triggered by some event
119                 LOG.warn("DataTreeCandidate for a notification is unmodified. Candidate: {}", candidate);
120                 return "none";
121             case SUBTREE_MODIFIED:
122             case WRITE:
123             case APPEARED:
124                 if (candidate.getDataBefore().isPresent()) {
125                     return "updated";
126                 } else {
127                     return "created";
128                 }
129             case DELETE:
130             case DISAPPEARED:
131                 return "deleted";
132             default:
133                 LOG.error("DataTreeCandidate modification has unknown type: {}",
134                         candidate.getModificationType());
135                 throw new IllegalStateException("Unknown modification type");
136         }
137     }
138 }