blob: bc959f0bef20d51d079092ef9704ce0ec4e9add0 [file] [log] [blame]
chengfanc58d4be2016-09-20 10:33:12 +08001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.protocol.restconf.server.utils.parser.json;
18
19import com.fasterxml.jackson.databind.node.ObjectNode;
20import com.google.common.base.Splitter;
21import com.google.common.collect.Lists;
22import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
23import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
chengfan7b2a60b2016-10-08 20:27:15 +080024import org.onosproject.protocol.restconf.server.utils.parser.api.NormalizedYangNode;
chengfanc58d4be2016-09-20 10:33:12 +080025import org.onosproject.yms.ydt.YdtBuilder;
26import org.onosproject.yms.ydt.YdtContext;
27import org.onosproject.yms.ydt.YdtContextOperationType;
28import org.onosproject.yms.ydt.YdtListener;
29import org.onosproject.yms.ydt.YdtType;
30import org.onosproject.yms.ydt.YdtWalker;
31
32import java.io.UnsupportedEncodingException;
33import java.net.URLDecoder;
34import java.util.ArrayList;
35import java.util.List;
36
37import static com.google.common.base.Preconditions.checkNotNull;
38import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
39
40/**
41 * Utils to complete the conversion between JSON and YDT(YANG DATA MODEL).
42 */
43public final class ParserUtils {
44
45 private static final Splitter SLASH_SPLITTER = Splitter.on('/');
46 private static final Splitter COMMA_SPLITTER = Splitter.on(',');
47 private static final String EQUAL = "=";
48 private static final String COMMA = ",";
49 private static final String COLON = ":";
50 private static final String URI_ENCODING_CHAR_SET = "ISO-8859-1";
51 private static final String ERROR_LIST_MSG = "List/Leaf-list node should be " +
52 "in format \"nodeName=key\"or \"nodeName=instance-value\"";
53 private static final String ERROR_MODULE_MSG = "First node should be in " +
54 "format \"moduleName:nodeName\"";
55
56 // no instantiation
57 private ParserUtils() {
58 }
59
60 /**
61 * Converts URI identifier to YDT builder.
62 *
63 * @param id the uri identifier from web request
64 * @param builder the base ydt builder
65 * @param opType the ydt operation type for the uri
66 */
67 public static void convertUriToYdt(String id,
68 YdtBuilder builder,
69 YdtContextOperationType opType) {
70 checkNotNull(id, "uri identifier should not be null");
71 List<String> paths = urlPathArgsDecode(SLASH_SPLITTER.split(id));
72 if (!paths.isEmpty()) {
73 processPathSegments(paths, builder, opType);
74 }
75 }
76
77 /**
78 * Converts JSON objectNode to YDT builder. The objectNode can be any
79 * standard JSON node, node just for RESTconf payload.
80 *
81 * @param objectNode the objectNode from web request
82 * @param builder the base ydt builder
83 */
84 public static void convertJsonToYdt(ObjectNode objectNode,
85 YdtBuilder builder) {
86
87 JsonToYdtListener listener = new JsonToYdtListener(builder);
88 new DefaultJsonWalker().walk(listener, null, objectNode);
89 }
90
91 /**
92 * Converts a Ydt context tree to a JSON object.
93 *
94 * @param rootName the name of the YdtContext from which the YdtListener
95 * start to builder a Json Object
96 * @param context a abstract data model for YANG data
97 * @param walker abstraction of an entity which provides interfaces for
98 * YDT walk
99 * @return the JSON node corresponding the YANG data
100 */
101 public static ObjectNode convertYdtToJson(String rootName,
102 YdtContext context,
103 YdtWalker walker) {
104 JsonBuilder builder = new DefaultJsonBuilder();
105 YdtListener listener = new YdtToJsonListener(rootName, builder);
106 walker.walk(listener, context);
107 return builder.getTreeNode();
108 }
109
110 /**
111 * Converts a list of path segments to a YDT builder tree.
112 *
113 * @param paths the list of path segments split from URI
114 * @param builder the base YDT builder
115 * @param opType the YDT operation type for the Path segment
116 * @return the YDT builder with the tree info of paths
117 */
118 private static YdtBuilder processPathSegments(List<String> paths,
119 YdtBuilder builder,
120 YdtContextOperationType opType) {
121 if (paths.isEmpty()) {
122 return builder;
123 }
124 boolean isLastNode = paths.size() == 1;
125 YdtContextOperationType opTypeForThisNode = isLastNode ? opType : NONE;
126
127 String path = paths.iterator().next();
128 if (path.contains(COLON)) {
129 addModule(builder, path);
130 addNode(path, builder, opTypeForThisNode);
131 } else if (path.contains(EQUAL)) {
132 addListOrLeafList(path, builder, opTypeForThisNode);
133 } else {
134 addLeaf(path, builder, opTypeForThisNode);
135 }
136
137 if (isLastNode) {
138 return builder;
139 }
140 List<String> remainPaths = paths.subList(1, paths.size());
141 processPathSegments(remainPaths, builder, opType);
142
143 return builder;
144 }
145
chengfanc58d4be2016-09-20 10:33:12 +0800146 private static YdtBuilder addListOrLeafList(String path,
147 YdtBuilder builder,
148 YdtContextOperationType opType) {
149 String nodeName = getPreSegment(path, EQUAL);
150 String keyStr = getLatterSegment(path, EQUAL);
151 if (keyStr == null) {
152 throw new JsonParseException(ERROR_LIST_MSG);
153 }
154 builder.setDefaultEditOperationType(opType);
155 if (keyStr.contains(COMMA)) {
156 List<String> keys = Lists.
157 newArrayList(COMMA_SPLITTER.split(keyStr));
158 builder.addMultiInstanceChild(nodeName, null, keys);
159 } else {
160 builder.addMultiInstanceChild(nodeName, null,
161 Lists.newArrayList(keyStr));
162 }
163 return builder;
164 }
165
166 private static YdtBuilder addLeaf(String path, YdtBuilder builder,
167 YdtContextOperationType opType) {
168 checkNotNull(path);
169 builder.addChild(path, null, opType);
170 return builder;
171 }
172
chengfan7b2a60b2016-10-08 20:27:15 +0800173 private static YdtBuilder addModule(YdtBuilder builder, String path) {
174 String moduleName = getPreSegment(path, COLON);
175 if (moduleName == null) {
176 throw new JsonParseException(ERROR_MODULE_MSG);
177 }
178 builder.addChild(moduleName, null, YdtType.SINGLE_INSTANCE_NODE);
179 return builder;
180 }
181
182 private static YdtBuilder addNode(String path, YdtBuilder builder,
183 YdtContextOperationType opType) {
184 String nodeName = getLatterSegment(path, COLON);
185 builder.addChild(nodeName,
186 null,
187 YdtType.SINGLE_INSTANCE_NODE,
188 opType);
189 return builder;
190 }
191
chengfanc58d4be2016-09-20 10:33:12 +0800192 /**
193 * Returns the previous segment of a path which is separated by a split char.
194 * For example:
195 * <pre>
196 * "foo:bar", ":" --> "foo"
197 * </pre>
198 *
199 * @param path the original path string
200 * @param splitChar char used to split the path
201 * @return the previous segment of the path
202 */
203 private static String getPreSegment(String path, String splitChar) {
204 int idx = path.indexOf(splitChar);
205 if (idx == -1) {
206 return null;
207 }
208
209 if (path.indexOf(splitChar, idx + 1) != -1) {
210 return null;
211 }
212
213 return path.substring(0, idx);
214 }
215
216 /**
217 * Returns the latter segment of a path which is separated by a split char.
218 * For example:
219 * <pre>
220 * "foo:bar", ":" --> "bar"
221 * </pre>
222 *
223 * @param path the original path string
224 * @param splitChar char used to split the path
225 * @return the latter segment of the path
226 */
227 private static String getLatterSegment(String path, String splitChar) {
228 int idx = path.indexOf(splitChar);
229 if (idx == -1) {
230 return path;
231 }
232
233 if (path.indexOf(splitChar, idx + 1) != -1) {
234 return null;
235 }
236
237 return path.substring(idx + 1);
238 }
239
240 /**
241 * Converts a list of path from the original format to ISO-8859-1 code.
242 *
243 * @param paths the original paths
244 * @return list of decoded paths
245 */
246 public static List<String> urlPathArgsDecode(Iterable<String> paths) {
247 try {
248 List<String> decodedPathArgs = new ArrayList<>();
249 for (String pathArg : paths) {
250 String decode = URLDecoder.decode(pathArg,
251 URI_ENCODING_CHAR_SET);
252 decodedPathArgs.add(decode);
253 }
254 return decodedPathArgs;
255 } catch (UnsupportedEncodingException e) {
256 throw new JsonParseException("Invalid URL path arg '" +
257 paths + "': ", e);
258 }
259 }
chengfan7b2a60b2016-10-08 20:27:15 +0800260
261 /**
262 * Converts a field to a simple YANG node description which contains the
263 * namespace and name information.
264 *
265 * @param field field name of a JSON body, or a segment of a URI
266 * in a request of RESTCONF
267 * @return a simple normalized YANG node
268 */
269 public static NormalizedYangNode buildNormalizedNode(String field) {
270 String namespace = getPreSegment(field, COLON);
271 String name = getLatterSegment(field, COLON);
272 return new NormalizedYangNode(namespace, name);
273 }
chengfanc58d4be2016-09-20 10:33:12 +0800274}