blob: 3a74bd2cdfa77692a7f08ee4187d4c495c9f3225 [file] [log] [blame]
jaegonkim6a7b5242018-09-12 23:09:42 +09001/*
2 * Copyright 2018-present Open Networking Foundation
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 */
16package org.onosproject.workflow.api;
17
18
19import com.fasterxml.jackson.core.JsonPointer;
20import com.fasterxml.jackson.core.JsonProcessingException;
21import com.fasterxml.jackson.databind.JsonNode;
22import com.fasterxml.jackson.databind.ObjectMapper;
23import com.fasterxml.jackson.databind.node.ArrayNode;
24import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25import com.fasterxml.jackson.databind.node.JsonNodeType;
26import com.fasterxml.jackson.databind.node.MissingNode;
27import com.fasterxml.jackson.databind.node.ObjectNode;
28import com.google.common.base.MoreObjects;
29
30import java.util.Objects;
31
32/**
33 * Class for json data model tree.
34 */
35public final class JsonDataModelTree implements DataModelTree {
36
37 /**
38 * Root node of json data model tree.
39 */
40 private JsonNode root;
41
42 /**
43 * Constructor of JsonDataModelTree.
44 */
45 public JsonDataModelTree() {
46 this.root = JsonNodeFactory.instance.objectNode();
47 }
48
49 /**
50 * Constructor of JsonDataModelTree.
51 * @param root root node of json data model tree
52 */
53 public JsonDataModelTree(JsonNode root) {
54 this.root = root;
55 }
56
57 @Override
58 public DataModelTree subtree(String path) {
59 JsonNode node = root.at(path);
60 if (Objects.isNull(node) || node.isMissingNode()) {
61 return null;
62 }
63 return new JsonDataModelTree(node);
64 }
65
66 @Override
67 public void attach(String path, DataModelTree tree) throws WorkflowException {
68 if (root == null || root instanceof MissingNode) {
69 throw new WorkflowException("Invalid root node");
70 }
71
72 JsonPointer ptr = JsonPointer.compile(path);
73 JsonNode node = root.at(ptr);
74 if (!(node instanceof MissingNode)) {
75 throw new WorkflowException("Path(" + path + ") has already subtree(" + node + ")");
76 }
77
78 if (!(tree instanceof JsonDataModelTree)) {
79 throw new WorkflowException("Invalid subTree(" + tree + ")");
80 }
81 JsonNode attachingNode = ((JsonDataModelTree) tree).root();
82
83 alloc(ptr.head(), Nodetype.MAP);
84 JsonNode parentNode = root.at(ptr.head());
85
86 if (!parentNode.isObject()) {
87 throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + ")");
88 }
89
90 String key = ptr.last().getMatchingProperty();
91 ((ObjectNode) parentNode).put(key, attachingNode);
92 }
93
94 @Override
95 public JsonDataModelTree alloc(String path, Nodetype leaftype) throws WorkflowException {
96 if (root == null || root instanceof MissingNode) {
97 throw new WorkflowException("Invalid root node");
98 }
99
100 JsonPointer ptr = JsonPointer.compile(path);
101 return alloc(ptr, leaftype);
102 }
103
104 /**
105 * Allocates json data model tree on json pointer path with specific leaf type.
106 * @param ptr json pointer to allocate
107 * @param leaftype type of leaf node
108 * @return json data model tree
109 * @throws WorkflowException workflow exception
110 */
111 private JsonDataModelTree alloc(JsonPointer ptr, Nodetype leaftype) throws WorkflowException {
112 if (root == null || root instanceof MissingNode) {
113 throw new WorkflowException("Invalid root node");
114 }
115
116 switch (leaftype) {
117 case MAP:
118 alloc(root, ptr, JsonNodeType.OBJECT);
119 break;
120 case ARRAY:
121 alloc(root, ptr, JsonNodeType.ARRAY);
122 break;
123 default:
124 throw new WorkflowException("Not supported leaftype(" + leaftype + ")");
125 }
126 return this;
127 }
128
129 /**
130 * Gets root json node.
131 * @return root json node
132 * @throws WorkflowException workflow exception
133 */
134 public JsonNode root() throws WorkflowException {
135 return nodeAt("");
136 }
137
138 /**
139 * Gets root json node as ObjectNode (MAP type).
140 * @return root json node as ObjectNode
141 * @throws WorkflowException workflow exception
142 */
143 public ObjectNode rootObject() throws WorkflowException {
144 return objectAt("");
145 }
146
147 /**
148 * Gets root json node as ArrayNode (Array type).
149 * @return root json node as ArrayNode
150 * @throws WorkflowException workflow exception
151 */
152 public ArrayNode rootArray() throws WorkflowException {
153 return arrayAt("");
154 }
155
156 /**
157 * Gets json node on specific path.
158 * @param path path of json node
159 * @return json node on specific path
160 * @throws WorkflowException workflow exception
161 */
162 public JsonNode nodeAt(String path) throws WorkflowException {
163 JsonPointer ptr = JsonPointer.compile(path);
164 return nodeAt(ptr);
165 }
166
167 /**
168 * Gets json node on specific json pointer.
169 * @param ptr json pointer
170 * @return json node on specific json pointer.
171 * @throws WorkflowException workflow exception
172 */
173 public JsonNode nodeAt(JsonPointer ptr) throws WorkflowException {
174 if (root == null || root instanceof MissingNode) {
175 throw new WorkflowException("Invalid root node");
176 }
177 JsonNode node = root.at(ptr);
178 return node;
179 }
180
181 /**
182 * Gets json node on specific path as ObjectNode.
183 * @param path path of json node
184 * @return ObjectNode type json node on specific path
185 * @throws WorkflowException workflow exception
186 */
187 public ObjectNode objectAt(String path) throws WorkflowException {
188 JsonPointer ptr = JsonPointer.compile(path);
189 return objectAt(ptr);
190 }
191
192 /**
193 * Gets json node on specific json pointer as ObjectNode.
194 * @param ptr json pointer
195 * @return ObjectNode type json node on specific json pointer.
196 * @throws WorkflowException workflow exception
197 */
198 public ObjectNode objectAt(JsonPointer ptr) throws WorkflowException {
199 if (root == null || root instanceof MissingNode) {
200 throw new WorkflowException("Invalid root node");
201 }
202 JsonNode node = root.at(ptr);
203 if (!(node instanceof ObjectNode)) {
204 throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
205 }
206 return (ObjectNode) node;
207 }
208
209 /**
210 * Gets json node on specific path as ArrayNode.
211 * @param path path of json node
212 * @return ArrayNode type json node on specific path
213 * @throws WorkflowException workflow exception
214 */
215 public ArrayNode arrayAt(String path) throws WorkflowException {
216 JsonPointer ptr = JsonPointer.compile(path);
217 return arrayAt(ptr);
218 }
219
220 /**
221 * Gets json node on specific json pointer as ArrayNode.
222 * @param ptr json pointer
223 * @return ArrayNode type json node on specific json pointer.
224 * @throws WorkflowException workflow exception
225 */
226 public ArrayNode arrayAt(JsonPointer ptr) throws WorkflowException {
227 if (root == null || root instanceof MissingNode) {
228 throw new WorkflowException("Invalid root node");
229 }
230 JsonNode node = root.at(ptr);
231 if (!(node instanceof ArrayNode)) {
232 throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
233 }
234 return (ArrayNode) node;
235 }
236
237 /**
238 * Allocates json data model tree on json pointer path with specific leaf type.
239 * @param node current json node in the json tree path
240 * @param ptr json pointer
241 * @param leaftype leaf type to be allocated
242 * @return allocated json node
243 * @throws WorkflowException workflow exception
244 */
245 private JsonNode alloc(JsonNode node, JsonPointer ptr, JsonNodeType leaftype) throws WorkflowException {
246
247 if (ptr.matches()) {
248 if (node instanceof MissingNode) {
249 node = createEmpty(leaftype);
250 } else {
251 //TODO: checking existing node type is matched with leaftype
252 if (Objects.equals(node.getNodeType(), leaftype)) {
253 throw new WorkflowException("Requesting leaftype(" + leaftype + ") is not matched with "
254 + "existing nodetype(" + node.getNodeType() + ") for " + ptr);
255 }
256 }
257 return node;
258 }
259
260 if (ptr.getMatchingIndex() != -1) {
261 if (node instanceof MissingNode) {
262 node = createEmpty(JsonNodeType.ARRAY);
263 }
264 JsonNode child = alloc(node.get(ptr.getMatchingIndex()), ptr.tail(), leaftype);
265 if (!node.has(ptr.getMatchingIndex())) {
266 ((ArrayNode) node).insert(ptr.getMatchingIndex(), child);
267 }
268 } else if (ptr.getMatchingProperty() != null) {
269 if (node instanceof MissingNode) {
270 node = createEmpty(JsonNodeType.OBJECT);
271 }
272 JsonNode child = alloc(node.get(ptr.getMatchingProperty()), ptr.tail(), leaftype);
273 if (!node.has(ptr.getMatchingProperty())) {
274 ((ObjectNode) node).put(ptr.getMatchingProperty(), child);
275 }
276 }
277 return node;
278 }
279
280 /**
281 * Creating empty json node.
282 * @param type json node type to create
283 * @return created json node
284 * @throws WorkflowException workflow exception
285 */
286 private JsonNode createEmpty(JsonNodeType type) throws WorkflowException {
287 if (type == JsonNodeType.OBJECT) {
288 return JsonNodeFactory.instance.objectNode();
289 } else if (type == JsonNodeType.ARRAY) {
290 return JsonNodeFactory.instance.arrayNode();
291 } else if (type == JsonNodeType.STRING) {
292 return JsonNodeFactory.instance.textNode("");
293 } else {
294 throw new WorkflowException("Not supported JsonNodetype(" + type + ")");
295 }
296 }
297
298 /**
299 * Gets the pretty json formatted string of this json data model tree.
300 * @return pretty json formatted string of this json data model tree
301 */
302 public String formattedRootString() {
303 String str = "";
304 try {
305 str = (new ObjectMapper()).writerWithDefaultPrettyPrinter().writeValueAsString(root);
306 } catch (JsonProcessingException e) {
307 e.printStackTrace();
308 }
309 return str;
310 }
311
312 @Override
313 public String toString() {
314 return MoreObjects.toStringHelper(getClass())
315 .add("json", formattedRootString())
316 .toString();
317 }
318}
319