blob: fa8b133fafe2eb3df56ba7e29e52dbcc6976c733 [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;
24import org.onosproject.yms.ydt.YdtBuilder;
25import org.onosproject.yms.ydt.YdtContext;
26import org.onosproject.yms.ydt.YdtContextOperationType;
27import org.onosproject.yms.ydt.YdtListener;
28import org.onosproject.yms.ydt.YdtType;
29import org.onosproject.yms.ydt.YdtWalker;
30
31import java.io.UnsupportedEncodingException;
32import java.net.URLDecoder;
33import java.util.ArrayList;
34import java.util.List;
35
36import static com.google.common.base.Preconditions.checkNotNull;
37import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
38
39/**
40 * Utils to complete the conversion between JSON and YDT(YANG DATA MODEL).
41 */
42public final class ParserUtils {
43
44 private static final Splitter SLASH_SPLITTER = Splitter.on('/');
45 private static final Splitter COMMA_SPLITTER = Splitter.on(',');
46 private static final String EQUAL = "=";
47 private static final String COMMA = ",";
48 private static final String COLON = ":";
49 private static final String URI_ENCODING_CHAR_SET = "ISO-8859-1";
50 private static final String ERROR_LIST_MSG = "List/Leaf-list node should be " +
51 "in format \"nodeName=key\"or \"nodeName=instance-value\"";
52 private static final String ERROR_MODULE_MSG = "First node should be in " +
53 "format \"moduleName:nodeName\"";
54
55 // no instantiation
56 private ParserUtils() {
57 }
58
59 /**
60 * Converts URI identifier to YDT builder.
61 *
62 * @param id the uri identifier from web request
63 * @param builder the base ydt builder
64 * @param opType the ydt operation type for the uri
65 */
66 public static void convertUriToYdt(String id,
67 YdtBuilder builder,
68 YdtContextOperationType opType) {
69 checkNotNull(id, "uri identifier should not be null");
70 List<String> paths = urlPathArgsDecode(SLASH_SPLITTER.split(id));
71 if (!paths.isEmpty()) {
72 processPathSegments(paths, builder, opType);
73 }
74 }
75
76 /**
77 * Converts JSON objectNode to YDT builder. The objectNode can be any
78 * standard JSON node, node just for RESTconf payload.
79 *
80 * @param objectNode the objectNode from web request
81 * @param builder the base ydt builder
82 */
83 public static void convertJsonToYdt(ObjectNode objectNode,
84 YdtBuilder builder) {
85
86 JsonToYdtListener listener = new JsonToYdtListener(builder);
87 new DefaultJsonWalker().walk(listener, null, objectNode);
88 }
89
90 /**
91 * Converts a Ydt context tree to a JSON object.
92 *
93 * @param rootName the name of the YdtContext from which the YdtListener
94 * start to builder a Json Object
95 * @param context a abstract data model for YANG data
96 * @param walker abstraction of an entity which provides interfaces for
97 * YDT walk
98 * @return the JSON node corresponding the YANG data
99 */
100 public static ObjectNode convertYdtToJson(String rootName,
101 YdtContext context,
102 YdtWalker walker) {
103 JsonBuilder builder = new DefaultJsonBuilder();
104 YdtListener listener = new YdtToJsonListener(rootName, builder);
105 walker.walk(listener, context);
106 return builder.getTreeNode();
107 }
108
109 /**
110 * Converts a list of path segments to a YDT builder tree.
111 *
112 * @param paths the list of path segments split from URI
113 * @param builder the base YDT builder
114 * @param opType the YDT operation type for the Path segment
115 * @return the YDT builder with the tree info of paths
116 */
117 private static YdtBuilder processPathSegments(List<String> paths,
118 YdtBuilder builder,
119 YdtContextOperationType opType) {
120 if (paths.isEmpty()) {
121 return builder;
122 }
123 boolean isLastNode = paths.size() == 1;
124 YdtContextOperationType opTypeForThisNode = isLastNode ? opType : NONE;
125
126 String path = paths.iterator().next();
127 if (path.contains(COLON)) {
128 addModule(builder, path);
129 addNode(path, builder, opTypeForThisNode);
130 } else if (path.contains(EQUAL)) {
131 addListOrLeafList(path, builder, opTypeForThisNode);
132 } else {
133 addLeaf(path, builder, opTypeForThisNode);
134 }
135
136 if (isLastNode) {
137 return builder;
138 }
139 List<String> remainPaths = paths.subList(1, paths.size());
140 processPathSegments(remainPaths, builder, opType);
141
142 return builder;
143 }
144
145 private static YdtBuilder addModule(YdtBuilder builder, String path) {
146 String moduleName = getPreSegment(path, COLON);
147 if (moduleName == null) {
148 throw new JsonParseException(ERROR_MODULE_MSG);
149 }
150 builder.addChild(moduleName, null, YdtType.SINGLE_INSTANCE_NODE);
151 return builder;
152 }
153
154 private static YdtBuilder addNode(String path, YdtBuilder builder,
155 YdtContextOperationType opType) {
156 String nodeName = getLatterSegment(path, COLON);
157 builder.addChild(nodeName,
158 null,
159 YdtType.SINGLE_INSTANCE_NODE,
160 opType);
161 return builder;
162 }
163
164 private static YdtBuilder addListOrLeafList(String path,
165 YdtBuilder builder,
166 YdtContextOperationType opType) {
167 String nodeName = getPreSegment(path, EQUAL);
168 String keyStr = getLatterSegment(path, EQUAL);
169 if (keyStr == null) {
170 throw new JsonParseException(ERROR_LIST_MSG);
171 }
172 builder.setDefaultEditOperationType(opType);
173 if (keyStr.contains(COMMA)) {
174 List<String> keys = Lists.
175 newArrayList(COMMA_SPLITTER.split(keyStr));
176 builder.addMultiInstanceChild(nodeName, null, keys);
177 } else {
178 builder.addMultiInstanceChild(nodeName, null,
179 Lists.newArrayList(keyStr));
180 }
181 return builder;
182 }
183
184 private static YdtBuilder addLeaf(String path, YdtBuilder builder,
185 YdtContextOperationType opType) {
186 checkNotNull(path);
187 builder.addChild(path, null, opType);
188 return builder;
189 }
190
191 /**
192 * Returns the previous segment of a path which is separated by a split char.
193 * For example:
194 * <pre>
195 * "foo:bar", ":" --> "foo"
196 * </pre>
197 *
198 * @param path the original path string
199 * @param splitChar char used to split the path
200 * @return the previous segment of the path
201 */
202 private static String getPreSegment(String path, String splitChar) {
203 int idx = path.indexOf(splitChar);
204 if (idx == -1) {
205 return null;
206 }
207
208 if (path.indexOf(splitChar, idx + 1) != -1) {
209 return null;
210 }
211
212 return path.substring(0, idx);
213 }
214
215 /**
216 * Returns the latter segment of a path which is separated by a split char.
217 * For example:
218 * <pre>
219 * "foo:bar", ":" --> "bar"
220 * </pre>
221 *
222 * @param path the original path string
223 * @param splitChar char used to split the path
224 * @return the latter segment of the path
225 */
226 private static String getLatterSegment(String path, String splitChar) {
227 int idx = path.indexOf(splitChar);
228 if (idx == -1) {
229 return path;
230 }
231
232 if (path.indexOf(splitChar, idx + 1) != -1) {
233 return null;
234 }
235
236 return path.substring(idx + 1);
237 }
238
239 /**
240 * Converts a list of path from the original format to ISO-8859-1 code.
241 *
242 * @param paths the original paths
243 * @return list of decoded paths
244 */
245 public static List<String> urlPathArgsDecode(Iterable<String> paths) {
246 try {
247 List<String> decodedPathArgs = new ArrayList<>();
248 for (String pathArg : paths) {
249 String decode = URLDecoder.decode(pathArg,
250 URI_ENCODING_CHAR_SET);
251 decodedPathArgs.add(decode);
252 }
253 return decodedPathArgs;
254 } catch (UnsupportedEncodingException e) {
255 throw new JsonParseException("Invalid URL path arg '" +
256 paths + "': ", e);
257 }
258 }
259}