blob: 89720f4858d872b813b4faf46ed9939a494d2949 [file] [log] [blame]
Rama-Huaweib711e5c2016-08-31 07:55:46 +05301/*
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.yms.app.yob;
18
19import org.onosproject.yangutils.datamodel.RpcNotificationContainer;
20import org.onosproject.yangutils.datamodel.YangNode;
21import org.onosproject.yangutils.datamodel.YangSchemaNode;
22import org.onosproject.yangutils.datamodel.YangSchemaNodeContextInfo;
23import org.onosproject.yangutils.datamodel.YangSchemaNodeIdentifier;
24import org.onosproject.yangutils.datamodel.exceptions.DataModelException;
25import org.onosproject.yms.app.ydt.YdtExtendedContext;
26import org.onosproject.yms.app.yob.exception.YobExceptions;
27import org.onosproject.yms.app.ysr.YangSchemaRegistry;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.lang.reflect.Field;
32import java.lang.reflect.InvocationTargetException;
33import java.lang.reflect.Method;
34import java.lang.reflect.ParameterizedType;
35import java.util.HashMap;
36import java.util.Map;
37
38import static org.onosproject.yangutils.datamodel.YangSchemaNodeType.YANG_AUGMENT_NODE;
39import static org.onosproject.yangutils.datamodel.YangSchemaNodeType.YANG_CHOICE_NODE;
40import static org.onosproject.yangutils.utils.io.impl.YangIoUtils.getCapitalCase;
41import static org.onosproject.yms.app.ydt.AppType.YOB;
42import static org.onosproject.yms.app.yob.YobConstants.ADD_TO;
43import static org.onosproject.yms.app.yob.YobConstants.BUILD;
44import static org.onosproject.yms.app.yob.YobConstants.DEFAULT;
45import static org.onosproject.yms.app.yob.YobConstants.FAIL_TO_BUILD;
46import static org.onosproject.yms.app.yob.YobConstants.FAIL_TO_GET_FIELD;
47import static org.onosproject.yms.app.yob.YobConstants.FAIL_TO_GET_METHOD;
48import static org.onosproject.yms.app.yob.YobConstants.FAIL_TO_INVOKE_METHOD;
49import static org.onosproject.yms.app.yob.YobConstants.HAS_NO_CHILD;
50import static org.onosproject.yms.app.yob.YobConstants.OPERATION_TYPE;
51import static org.onosproject.yms.app.yob.YobConstants.OP_PARAM;
52import static org.onosproject.yms.app.yob.YobConstants.OP_TYPE;
53import static org.onosproject.yms.app.yob.YobConstants.PERIOD;
54import static org.onosproject.yms.app.yob.YobConstants.SET_OP_TYPE_FAIL;
55import static org.onosproject.yms.app.yob.YobConstants.VALUE_OF;
56import static org.onosproject.yms.ydt.YdtType.MULTI_INSTANCE_NODE;
57
58/**
59 * Represents the YANG object builder's work bench corresponding to a YANG data
60 * tree node.
61 */
62class YobWorkBench {
63
64 private static final Logger log
65 = LoggerFactory.getLogger(YobWorkBench.class);
66
67 /**
68 * Class loader to be used to load the class.
69 */
70 private ClassLoader classLoader;
71
72 /**
73 * Map of the non schema descendant objects.
74 */
75 private Map<YangSchemaNodeIdentifier, YobWorkBench> attributeMap
76 = new HashMap<>();
77
78 /**
79 * Reference for data-model schema node.
80 */
81 private YangSchemaNode yangSchemaNode;
82
83 /**
84 * builder object or the built object corresponding to the current schema
85 * node.
86 */
87 private YobBuilderOrBuiltObject builderOrBuiltObject;
88
89 /**
90 * Setter method to be used in parent builder.
91 */
92 private String setterInParent;
93
94 /**
95 * Returns the builder container with the mapping schema being initialized.
96 *
97 * @param yangSchemaNode mapping schema node
98 * @param classLoader class loader
99 * @param qualifiedClassName qualified class name
100 * @param setterInParent setter method in parent
101 */
102 YobWorkBench(YangSchemaNode yangSchemaNode, ClassLoader classLoader,
103 String qualifiedClassName, String setterInParent) {
104 this.yangSchemaNode = yangSchemaNode;
105 this.classLoader = classLoader;
106 this.setterInParent = setterInParent;
107 this.builderOrBuiltObject
108 = new YobBuilderOrBuiltObject(qualifiedClassName, classLoader);
109 }
110
111 /**
112 * Returns the builder object or the built object corresponding to the
113 * current schema node.
114 *
115 * @return builder or built object
116 */
117 YobBuilderOrBuiltObject getBuilderOrBuiltObject() {
118 return builderOrBuiltObject;
119 }
120
121 /**
122 * Returns the parent builder object in which the child object can be set.
123 *
124 * @param childNode child YDT node
125 * @param registry schema registry
126 * @return parent builder object
127 * @throws YobExceptions schema node does not have child
128 */
129 Object getParentBuilder(YdtExtendedContext childNode,
130 YangSchemaRegistry registry) {
131
132 // Descendant schema node for whom the builder is required.
133 YangSchemaNodeIdentifier targetNode = childNode
134 .getYangSchemaNode().getYangSchemaNodeIdentifier();
135
136 //Current builder container
137 YobWorkBench curWorkBench = this;
138
139 //Current Schema node context
140 YangSchemaNodeContextInfo schemaContext;
141 do {
142
143 try {
144 //Find the new schema context node.
145 schemaContext = curWorkBench.yangSchemaNode.getChildSchema(
146 targetNode);
147
148 } catch (DataModelException e) {
149 throw new YobExceptions(yangSchemaNode.getName() +
150 HAS_NO_CHILD +
151 targetNode.getName());
152 }
153
154 //If the descendant schema node is in switched context
155 if (schemaContext.getContextSwitchedNode() != null) {
156
157 //check if the descendant builder container is already available
158 YobWorkBench childWorkBench
159 = curWorkBench.attributeMap.get(targetNode);
160
161 if (childWorkBench == null) {
162 YobWorkBench newWorkBench = getNewChildWorkBench(
163 schemaContext, targetNode, curWorkBench, registry);
164
165 //TODO: When choice and case support is added, confirm with
166 // UT, the workbench is for case and not for choice
167
168 curWorkBench.attributeMap.put(targetNode, newWorkBench);
169 curWorkBench = newWorkBench;
170 } else {
171 curWorkBench = childWorkBench;
172 }
173 }
174
175 } while (schemaContext.getContextSwitchedNode() != null);
176
177 return curWorkBench.builderOrBuiltObject.getBuilderObject();
178 }
179
180 /**
181 * Creates a new builder container object corresponding to a context
182 * switch schema node.
183 *
184 * @param childContext schema context of immediate child
185 * @param targetNode final node whose parent builder is
186 * required
187 * @param curWorkBench current context builder container
188 * @param registry schema registry
189 * @return new builder container object corresponding to a context
190 * switch schema node
191 */
192 private YobWorkBench getNewChildWorkBench(
193 YangSchemaNodeContextInfo childContext,
194 YangSchemaNodeIdentifier targetNode, YobWorkBench curWorkBench,
195 YangSchemaRegistry registry) {
196
197 YangSchemaNode ctxSwitchedNode = childContext.getContextSwitchedNode();
198
199 /*This is the first child trying to set its object in the
200 current context. */
201 String setterInParent = ctxSwitchedNode.getJavaAttributeName();
202
203 /* If current switched context is choice, then case class needs to be
204 used. */
205 if (ctxSwitchedNode.getYangSchemaNodeType() == YANG_CHOICE_NODE) {
206 try {
207 childContext = ctxSwitchedNode.getChildSchema(targetNode);
208 } catch (DataModelException e) {
209 throw new YobExceptions(yangSchemaNode.getName() +
210 HAS_NO_CHILD +
211 targetNode.getName());
212 }
213 }
214
215 ClassLoader newClassesLoader = getTargetClassLoader(
216 curWorkBench.classLoader, childContext, registry);
217
218 return new YobWorkBench(ctxSwitchedNode, newClassesLoader,
219 getQualifiedDefaultClassName(
220 childContext.getSchemaNode()),
221 setterInParent);
222 }
223
224 /**
225 * Returns the qualified default / op param class.
226 *
227 * @param schemaNode schema node of the required class
228 * @return qualified default / op param class name
229 */
230 static String getQualifiedDefaultClassName(YangSchemaNode schemaNode) {
231 String packageName = schemaNode.getJavaPackage();
232 String className = getCapitalCase(
233 schemaNode.getJavaClassNameOrBuiltInType());
234
235 if (schemaNode instanceof RpcNotificationContainer) {
236 return packageName + PERIOD + className + OP_PARAM;
237 }
238
239 return packageName + PERIOD + DEFAULT + className;
240 }
241
242 /**
243 * Returns the class loader to be used for the switched context schema node.
244 *
245 * @param currentClassLoader current context class loader
246 * @param switchedContext switched context
247 * @param registry schema registry
248 * @return class loader to be used for the switched context schema node
249 */
250 private ClassLoader getTargetClassLoader(
251 ClassLoader currentClassLoader,
252 YangSchemaNodeContextInfo switchedContext,
253 YangSchemaRegistry registry) {
254 YangSchemaNode augmentSchemaNode = switchedContext.getSchemaNode();
255 if (augmentSchemaNode.getYangSchemaNodeType() ==
256 YANG_AUGMENT_NODE) {
257 YangSchemaNode parentSchemaNode =
258 ((YangNode) augmentSchemaNode).getParent();
259
260 Class<?> regClass = registry.getRegisteredClass(
261 parentSchemaNode, getCapitalCase(
262 parentSchemaNode.getJavaClassNameOrBuiltInType()));
263 return regClass.getClassLoader();
264 }
265
266 return currentClassLoader;
267 }
268
269 /**
270 * Set the operation type attribute and build the object from the builder
271 * object, by invoking the build method.
272 *
273 * @param ydtNode data tree node
274 * @param ydtRootNode root node
275 */
276 void buildObject(YdtExtendedContext ydtNode,
277 YdtExtendedContext ydtRootNode) {
278 Object builderObject = builderOrBuiltObject.getBuilderObject();
279 Class<?> defaultBuilderClass = builderOrBuiltObject.yangBuilderClass;
280 Class<?> interfaceClass = builderOrBuiltObject.yangDefaultClass;
281 Object operationType;
282
283 // Setting the value into YANG node operation type from ydtContext
284 // operation type.
285 try {
286 Class<?>[] innerClasses = interfaceClass.getClasses();
287 for (Class<?> innerEnumClass : innerClasses) {
288 if (innerEnumClass.getSimpleName().equals(OP_TYPE)) {
289 Method valueOfMethod = innerEnumClass
290 .getDeclaredMethod(VALUE_OF, String.class);
291 if (ydtNode.getYdtContextOperationType() != null) {
292 operationType = valueOfMethod.invoke(null, ydtNode
293 .getYdtContextOperationType().toString());
294 Field operationTypeField = defaultBuilderClass
295 .getDeclaredField(OPERATION_TYPE);
296 operationTypeField.setAccessible(true);
297 operationTypeField.set(builderObject, operationType);
298 break;
299 }
300 }
301 }
302 } catch (NoSuchFieldException | NoSuchMethodException |
303 InvocationTargetException | IllegalAccessException e) {
304 log.error(SET_OP_TYPE_FAIL);
305 throw new YobExceptions(SET_OP_TYPE_FAIL);
306 }
307
308
309 // Invoking the build method to get built object from build method.
310 try {
311 Method method = defaultBuilderClass.getDeclaredMethod(BUILD);
312 if (method == null) {
313 log.error(FAIL_TO_GET_METHOD + defaultBuilderClass.getName());
314 throw new YobExceptions(FAIL_TO_GET_METHOD +
315 defaultBuilderClass.getName());
316 }
317 Object builtObject = method.invoke(builderObject);
318 // The built object will be maintained in ydt context and same will
319 // be used while setting into parent method.
320 builderOrBuiltObject.setBuiltObject(builtObject);
321
322 } catch (NoSuchMethodException | InvocationTargetException |
323 IllegalAccessException e) {
324 log.error(FAIL_TO_BUILD + defaultBuilderClass.getName());
325 throw new YobExceptions(FAIL_TO_BUILD +
326 defaultBuilderClass.getName());
327 }
328
329 // The current ydt context node and root node are same then return.
330 if (!ydtNode.equals(ydtRootNode)) {
331 invokeSetObjectInParent(ydtNode);
332 }
333 }
334
335 /**
336 * Sets the YANG built object in corresponding parent class method.
337 *
338 * @param ydtNode ydtExtendedContext is used to get application
339 * related information maintained in YDT
340 */
341 private void invokeSetObjectInParent(YdtExtendedContext ydtNode) {
342 Class<?> classType = null;
343 Method method;
344
345 Object objectToSetInParent = builderOrBuiltObject.getBuiltObject();
346
347 YdtExtendedContext parentNode = (YdtExtendedContext) ydtNode
348 .getParent();
349 if (parentNode != null) {
350 YobWorkBench parentYobWorkBench = (YobWorkBench)
351 parentNode.getAppInfo(YOB);
352 Object parentBuilderObject = parentYobWorkBench
353 .builderOrBuiltObject.getBuilderObject();
354
355 Class<?> parentBuilderClass = parentBuilderObject.getClass();
356 String parentBuilderClassName = parentBuilderClass.getName();
357
358 try {
359 Field fieldName = parentBuilderClass
360 .getDeclaredField(setterInParent);
361 if (fieldName != null) {
362 classType = fieldName.getType();
363 }
364
365 if (ydtNode.getYdtType() == MULTI_INSTANCE_NODE) {
366 if (fieldName != null) {
367 ParameterizedType genericListType =
368 (ParameterizedType) fieldName.getGenericType();
369 classType = (Class<?>) genericListType
370 .getActualTypeArguments()[0];
371 }
372 method = parentBuilderClass.getDeclaredMethod(
373 ADD_TO + getCapitalCase(setterInParent), classType);
374 } else {
375 method = parentBuilderClass.getDeclaredMethod(
376 setterInParent, classType);
377 }
378
379 if (method != null) {
380 method.invoke(parentBuilderObject, objectToSetInParent);
381 }
382 } catch (NoSuchFieldException e) {
383 log.error(FAIL_TO_GET_FIELD + parentBuilderClassName);
384 } catch (NoSuchMethodException e) {
385 log.error(FAIL_TO_GET_METHOD + parentBuilderClassName);
386 } catch (InvocationTargetException | IllegalAccessException e) {
387 log.error(FAIL_TO_INVOKE_METHOD + parentBuilderClassName);
388 }
389 }
390 ydtNode.addAppInfo(YOB, this);
391 }
392}