| /* |
| * Copyright 2016-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.yms.app.yob; |
| |
| import org.onosproject.yangutils.datamodel.YangSchemaNode; |
| import org.onosproject.yangutils.datamodel.YangSchemaNodeContextInfo; |
| import org.onosproject.yangutils.datamodel.YangSchemaNodeIdentifier; |
| import org.onosproject.yangutils.datamodel.exceptions.DataModelException; |
| import org.onosproject.yms.app.ydt.YdtExtendedContext; |
| import org.onosproject.yms.app.yob.exception.YobException; |
| import org.onosproject.yms.app.ysr.YangSchemaRegistry; |
| import org.onosproject.yms.ydt.YdtContextOperationType; |
| import org.onosproject.yms.ydt.YdtType; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.ParameterizedType; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import static org.onosproject.yangutils.datamodel.YangSchemaNodeType.YANG_AUGMENT_NODE; |
| import static org.onosproject.yangutils.datamodel.YangSchemaNodeType.YANG_CHOICE_NODE; |
| import static org.onosproject.yms.app.ydt.AppType.YOB; |
| import static org.onosproject.yms.app.yob.YobConstants.ADD_AUGMENT_METHOD; |
| import static org.onosproject.yms.app.yob.YobConstants.ADD_TO; |
| import static org.onosproject.yms.app.yob.YobConstants.BUILD; |
| import static org.onosproject.yms.app.yob.YobConstants.E_FAIL_TO_BUILD; |
| import static org.onosproject.yms.app.yob.YobConstants.E_FAIL_TO_GET_FIELD; |
| import static org.onosproject.yms.app.yob.YobConstants.E_FAIL_TO_GET_METHOD; |
| import static org.onosproject.yms.app.yob.YobConstants.E_FAIL_TO_INVOKE_METHOD; |
| import static org.onosproject.yms.app.yob.YobConstants.E_FAIL_TO_LOAD_CLASS; |
| import static org.onosproject.yms.app.yob.YobConstants.E_HAS_NO_CHILD; |
| import static org.onosproject.yms.app.yob.YobConstants.E_SET_OP_TYPE_FAIL; |
| import static org.onosproject.yms.app.yob.YobConstants.L_FAIL_TO_BUILD; |
| import static org.onosproject.yms.app.yob.YobConstants.L_FAIL_TO_GET_FIELD; |
| import static org.onosproject.yms.app.yob.YobConstants.L_FAIL_TO_GET_METHOD; |
| import static org.onosproject.yms.app.yob.YobConstants.L_FAIL_TO_INVOKE_METHOD; |
| import static org.onosproject.yms.app.yob.YobConstants.ONOS_YANG_OP_TYPE; |
| import static org.onosproject.yms.app.yob.YobConstants.OP_TYPE; |
| import static org.onosproject.yms.app.yob.YobConstants.VALUE_OF; |
| import static org.onosproject.yms.app.yob.YobConstants.YANG; |
| import static org.onosproject.yms.app.yob.YobUtils.getCapitalCase; |
| import static org.onosproject.yms.app.yob.YobUtils.getModuleInterface; |
| import static org.onosproject.yms.app.yob.YobUtils.getQualifiedDefaultClass; |
| import static org.onosproject.yms.ydt.YdtType.MULTI_INSTANCE_NODE; |
| import static org.onosproject.yms.ydt.YdtType.SINGLE_INSTANCE_NODE; |
| |
| /** |
| * Represents the YANG object builder's work bench corresponding to a YANG data |
| * tree node. |
| */ |
| class YobWorkBench { |
| |
| private static final Logger log = |
| LoggerFactory.getLogger(YobWorkBench.class); |
| |
| /** |
| * Class loader to be used to load the class. |
| */ |
| private ClassLoader classLoader; |
| |
| /** |
| * Map of the non schema descendant objects. |
| */ |
| private Map<YangSchemaNodeIdentifier, YobWorkBench> attributeMap = |
| new HashMap<>(); |
| |
| /** |
| * Reference for data-model schema node. |
| */ |
| private YangSchemaNode yangSchemaNode; |
| |
| /** |
| * builder object or the built object corresponding to the current schema |
| * node. |
| */ |
| private YobBuilderOrBuiltObject builderOrBuiltObject; |
| |
| /** |
| * Setter method to be used in parent builder. |
| */ |
| private String setterInParent; |
| |
| /** |
| * Returns the builder container with the mapping schema being initialized. |
| * |
| * @param yangSchemaNode mapping schema node |
| * @param classLoader class loader |
| * @param qualifiedClassName qualified class name |
| * @param setterInParent setter method in parent |
| */ |
| YobWorkBench(YangSchemaNode yangSchemaNode, ClassLoader classLoader, |
| String qualifiedClassName, String setterInParent) { |
| this.yangSchemaNode = yangSchemaNode; |
| this.classLoader = classLoader; |
| this.setterInParent = setterInParent; |
| this.builderOrBuiltObject = |
| new YobBuilderOrBuiltObject(qualifiedClassName, classLoader); |
| } |
| |
| /** |
| * Set the attribute in a builder object. |
| * |
| * @param builder builder object in which the attribute needs to be set |
| * @param setter setter method in parent |
| * @param nodeType type of node to set |
| * @param attribute attribute to set in the builder |
| */ |
| private static void setObjectInBuilder(Object builder, String setter, |
| YdtType nodeType, Object attribute) { |
| Class<?> builderClass = builder.getClass(); |
| String builderClassName = builderClass.getName(); |
| try { |
| Class<?> type = null; |
| Field fieldName = builderClass.getDeclaredField(setter); |
| if (fieldName != null) { |
| type = fieldName.getType(); |
| } |
| |
| Method method; |
| if (nodeType == MULTI_INSTANCE_NODE) { |
| if (fieldName != null) { |
| ParameterizedType genericTypes = |
| (ParameterizedType) fieldName.getGenericType(); |
| type = (Class<?>) genericTypes.getActualTypeArguments()[0]; |
| } |
| method = builderClass.getDeclaredMethod( |
| ADD_TO + getCapitalCase(setter), type); |
| } else { |
| method = builderClass.getDeclaredMethod(setter, type); |
| } |
| |
| method.invoke(builder, attribute); |
| } catch (NoSuchFieldException e) { |
| log.error(L_FAIL_TO_GET_FIELD, builderClassName); |
| throw new YobException(E_FAIL_TO_GET_FIELD + builderClassName); |
| } catch (NoSuchMethodException e) { |
| log.error(L_FAIL_TO_GET_METHOD, builderClassName); |
| throw new YobException(E_FAIL_TO_GET_METHOD + builderClassName); |
| } catch (InvocationTargetException | IllegalAccessException e) { |
| log.error(L_FAIL_TO_INVOKE_METHOD, builderClassName); |
| throw new YobException(E_FAIL_TO_INVOKE_METHOD + builderClassName); |
| } |
| } |
| |
| private static void addInAugmentation(Object builder, String className, |
| Object instance) { |
| Class<?>[] interfaces = instance.getClass().getInterfaces(); |
| if (interfaces == null) { |
| throw new YobException(E_FAIL_TO_LOAD_CLASS + className); |
| } |
| |
| int i; |
| for (i = 0; i < interfaces.length; i++) { |
| if (interfaces[i].getName().equals(className)) { |
| break; |
| } |
| } |
| if (i == interfaces.length) { |
| throw new YobException(E_FAIL_TO_LOAD_CLASS + className); |
| } |
| |
| Class<?> builderClass = builder.getClass(); |
| String builderClassName = builderClass.getName(); |
| try { |
| |
| Method method = builderClass.getDeclaredMethod(ADD_AUGMENT_METHOD, |
| Object.class, |
| Class.class); |
| method.invoke(builder, instance, interfaces[i]); |
| } catch (NoSuchMethodException e) { |
| log.error(L_FAIL_TO_GET_METHOD, builderClassName); |
| throw new YobException(E_FAIL_TO_GET_METHOD + builderClassName); |
| } catch (InvocationTargetException | IllegalAccessException e) { |
| log.error(L_FAIL_TO_INVOKE_METHOD, builderClassName); |
| throw new YobException(E_FAIL_TO_INVOKE_METHOD + builderClassName); |
| } |
| |
| } |
| |
| /** |
| * Creates a new builder container object corresponding to a context |
| * switch schema node. |
| * |
| * @param childContext schema context of immediate child |
| * @param targetNode final node whose parent builder is |
| * required |
| * @param curWorkBench current context builder container |
| * @param registry schema registry |
| * @return new builder container object corresponding to a context |
| * switch schema node |
| */ |
| private static YobWorkBench getNewChildWorkBench( |
| YangSchemaNodeContextInfo childContext, |
| YangSchemaNodeIdentifier targetNode, YobWorkBench curWorkBench, |
| YangSchemaRegistry registry) { |
| |
| YangSchemaNode ctxSwitchedNode = childContext.getContextSwitchedNode(); |
| String name; |
| |
| /* This is the first child trying to set its object in the |
| current context. */ |
| String setterInParent = ctxSwitchedNode.getJavaAttributeName(); |
| |
| /* If current switched context is choice, then case class needs to be |
| used. */ |
| if (ctxSwitchedNode.getYangSchemaNodeType() == YANG_CHOICE_NODE) { |
| try { |
| childContext = ctxSwitchedNode.getChildSchema(targetNode); |
| ctxSwitchedNode = childContext.getContextSwitchedNode(); |
| name = getQualifiedDefaultClass( |
| childContext.getContextSwitchedNode()); |
| |
| } catch (DataModelException e) { |
| throw new YobException(ctxSwitchedNode.getName() + |
| E_HAS_NO_CHILD + |
| targetNode.getName()); |
| } |
| } else if (ctxSwitchedNode.getYangSchemaNodeType() == |
| YANG_AUGMENT_NODE) { |
| name = getQualifiedDefaultClass(ctxSwitchedNode); |
| setterInParent = YobUtils.getQualifiedinterface(ctxSwitchedNode); |
| } else { |
| name = getQualifiedDefaultClass(childContext.getSchemaNode()); |
| } |
| |
| ClassLoader newClassesLoader = YobUtils.getTargetClassLoader( |
| curWorkBench.classLoader, childContext, registry); |
| |
| return new YobWorkBench(ctxSwitchedNode, newClassesLoader, name, |
| setterInParent); |
| } |
| |
| /** |
| * Returns the builder object or the built object corresponding to the |
| * current schema node. |
| * |
| * @return builder or built object |
| */ |
| YobBuilderOrBuiltObject getBuilderOrBuiltObject() { |
| return builderOrBuiltObject; |
| } |
| |
| /** |
| * Returns the parent builder object in which the child object can be set. |
| * |
| * @param node child YDT node |
| * @param registry schema registry |
| * @return parent builder object |
| */ |
| Object getParentBuilder(YdtExtendedContext node, |
| YangSchemaRegistry registry) { |
| |
| // Descendant schema node for whom the builder is required. |
| YangSchemaNodeIdentifier targetNode = |
| node.getYangSchemaNode().getYangSchemaNodeIdentifier(); |
| |
| //Current builder container |
| YobWorkBench curWorkBench = this; |
| |
| YangSchemaNode nonSchemaHolder; |
| do { |
| |
| //Current Schema node context |
| YangSchemaNodeContextInfo schemaContext; |
| try { |
| //Find the new schema context node. |
| schemaContext = curWorkBench.yangSchemaNode |
| .getChildSchema(targetNode); |
| |
| } catch (DataModelException e) { |
| throw new YobException(yangSchemaNode.getName() + |
| E_HAS_NO_CHILD + |
| targetNode.getName()); |
| } |
| |
| nonSchemaHolder = schemaContext.getContextSwitchedNode(); |
| |
| //If the descendant schema node is in switched context |
| if (nonSchemaHolder != null) { |
| |
| YangSchemaNodeIdentifier nonSchemaIdentifier = |
| nonSchemaHolder.getYangSchemaNodeIdentifier(); |
| |
| //check if the descendant builder container is already available |
| YobWorkBench childWorkBench = |
| curWorkBench.attributeMap.get(nonSchemaIdentifier); |
| |
| if (childWorkBench == null) { |
| YobWorkBench newWorkBench = getNewChildWorkBench( |
| schemaContext, targetNode, curWorkBench, registry); |
| |
| curWorkBench.attributeMap.put(nonSchemaIdentifier, |
| newWorkBench); |
| curWorkBench = newWorkBench; |
| } else { |
| curWorkBench = childWorkBench; |
| } |
| } |
| |
| } while (nonSchemaHolder != null); |
| |
| return curWorkBench.builderOrBuiltObject.getBuilderObject(); |
| } |
| |
| /** |
| * Set the operation type attribute and build the object from the builder |
| * object, by invoking the build method. |
| * |
| * @param operationType data tree node |
| * @param schemaRegistry YANG schema registry |
| */ |
| void buildObject(YdtContextOperationType operationType, |
| YangSchemaRegistry schemaRegistry) { |
| |
| buildNonSchemaAttributes(operationType, schemaRegistry); |
| |
| Object builderObject = builderOrBuiltObject.getBuilderObject(); |
| Class<?> defaultBuilderClass = builderOrBuiltObject.yangBuilderClass; |
| |
| //set the operation type |
| setOperationType(operationType, schemaRegistry); |
| |
| // Invoking the build method to get built object from build method. |
| try { |
| Method method = defaultBuilderClass.getDeclaredMethod(BUILD); |
| if (method == null) { |
| log.error(L_FAIL_TO_GET_METHOD, defaultBuilderClass.getName()); |
| throw new YobException(E_FAIL_TO_GET_METHOD + |
| defaultBuilderClass.getName()); |
| } |
| Object builtObject = method.invoke(builderObject); |
| // The built object will be maintained in ydt context and same will |
| // be used while setting into parent method. |
| builderOrBuiltObject.setBuiltObject(builtObject); |
| |
| } catch (NoSuchMethodException | InvocationTargetException | |
| IllegalAccessException e) { |
| log.error(L_FAIL_TO_BUILD, defaultBuilderClass.getName()); |
| throw new YobException(E_FAIL_TO_BUILD + |
| defaultBuilderClass.getName()); |
| } |
| } |
| |
| /** |
| * Set the operation type in the built object from the YDT node. |
| * <p> |
| * It needs to be invoked only for the workbench corresponding to the |
| * schema YDT nodes, non schema node without the YDT node should not |
| * invoke this, as it is not applicable to it. |
| * |
| * @param ydtoperation schema data tree node |
| * @param schemaRegistry YANG schema registry |
| */ |
| private void setOperationType(YdtContextOperationType ydtoperation, |
| YangSchemaRegistry schemaRegistry) { |
| |
| if (ydtoperation == null) { |
| return; |
| } |
| |
| Object builderObject = builderOrBuiltObject.getBuilderObject(); |
| Class<?> defaultBuilderClass = builderOrBuiltObject.yangBuilderClass; |
| Class<?>[] intfClass = builderOrBuiltObject.yangDefaultClass |
| .getInterfaces(); |
| String setterName = YANG + intfClass[0].getSimpleName() + OP_TYPE; |
| |
| // Setting the value into YANG node operation type from ydtContext |
| // operation type. |
| try { |
| Class<?> interfaceClass; |
| interfaceClass = getModuleInterface(yangSchemaNode, |
| schemaRegistry); |
| Object operationType; |
| Class<?>[] innerClasses = interfaceClass.getClasses(); |
| for (Class<?> innerEnumClass : innerClasses) { |
| if (innerEnumClass.getSimpleName().equals(ONOS_YANG_OP_TYPE)) { |
| Method valueOfMethod = innerEnumClass |
| .getDeclaredMethod(VALUE_OF, String.class); |
| operationType = valueOfMethod.invoke(null, ydtoperation. |
| toString()); |
| Field operationTypeField = defaultBuilderClass |
| .getDeclaredField(setterName); |
| operationTypeField.setAccessible(true); |
| operationTypeField.set(builderObject, operationType); |
| break; |
| } |
| } |
| } catch (NoSuchMethodException | |
| InvocationTargetException | IllegalAccessException | |
| IllegalArgumentException e) { |
| log.error(E_SET_OP_TYPE_FAIL); |
| throw new YobException(E_SET_OP_TYPE_FAIL); |
| } catch (NoSuchFieldException e) { |
| log.error(E_SET_OP_TYPE_FAIL); |
| } |
| } |
| |
| /** |
| * build the non schema objects and maintain it in the contained schema |
| * node. |
| * |
| * @param operationType contained schema node |
| * @param schemaRegistry YANG schema registry |
| */ |
| private void buildNonSchemaAttributes(YdtContextOperationType operationType, |
| YangSchemaRegistry schemaRegistry) { |
| for (Map.Entry<YangSchemaNodeIdentifier, YobWorkBench> entry : |
| attributeMap.entrySet()) { |
| YobWorkBench childWorkBench = entry.getValue(); |
| childWorkBench.buildObject(operationType, schemaRegistry); |
| |
| if (childWorkBench.yangSchemaNode.getYangSchemaNodeType() == |
| YANG_AUGMENT_NODE) { |
| addInAugmentation(builderOrBuiltObject.getBuilderObject(), |
| childWorkBench.setterInParent, |
| childWorkBench.getBuilderOrBuiltObject() |
| .getBuiltObject()); |
| continue; |
| } |
| |
| setObjectInBuilder( |
| builderOrBuiltObject.getBuilderObject(), |
| childWorkBench.setterInParent, |
| SINGLE_INSTANCE_NODE, |
| childWorkBench.getBuilderOrBuiltObject().getBuiltObject()); |
| } |
| } |
| |
| /** |
| * Sets the YANG built object in corresponding parent class method. |
| * |
| * @param childnode ydtExtendedContext is used to get application |
| * related information maintained in YDT |
| * @param schemaRegistry YANG schema registry |
| */ |
| public void setObject(YdtExtendedContext childnode, |
| YangSchemaRegistry schemaRegistry) { |
| Object builder = getParentBuilder(childnode, schemaRegistry); |
| YobWorkBench childWorkBench = (YobWorkBench) childnode.getAppInfo(YOB); |
| |
| setObjectInBuilder(builder, childWorkBench.setterInParent, |
| childnode.getYdtType(), childWorkBench |
| .builderOrBuiltObject.getBuiltObject()); |
| } |
| } |