blob: 3bff8721d53d259baccfe224d95cd099740b52cc [file] [log] [blame]
/*
* Copyright 2017-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.yang.runtime.impl;
import org.onosproject.yang.compiler.datamodel.TraversalType;
import org.onosproject.yang.compiler.datamodel.YangAugment;
import org.onosproject.yang.compiler.datamodel.YangCase;
import org.onosproject.yang.compiler.datamodel.YangDerivedInfo;
import org.onosproject.yang.compiler.datamodel.YangIdentity;
import org.onosproject.yang.compiler.datamodel.YangIdentityRef;
import org.onosproject.yang.compiler.datamodel.YangLeaf;
import org.onosproject.yang.compiler.datamodel.YangLeafList;
import org.onosproject.yang.compiler.datamodel.YangLeafRef;
import org.onosproject.yang.compiler.datamodel.YangNode;
import org.onosproject.yang.compiler.datamodel.YangNotification;
import org.onosproject.yang.compiler.datamodel.YangOutput;
import org.onosproject.yang.compiler.datamodel.YangRpc;
import org.onosproject.yang.compiler.datamodel.YangSchemaNode;
import org.onosproject.yang.compiler.datamodel.YangType;
import org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes;
import org.onosproject.yang.model.AtomicPath;
import org.onosproject.yang.model.LeafSchemaContext;
import org.onosproject.yang.model.ModelConverterException;
import org.onosproject.yang.model.MultiInstanceLeaf;
import org.onosproject.yang.model.MultiInstanceNode;
import org.onosproject.yang.model.SingleInstanceLeaf;
import org.onosproject.yang.model.SingleInstanceNode;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static org.onosproject.yang.compiler.datamodel.TraversalType.PARENT;
import static org.onosproject.yang.compiler.datamodel.YangSchemaNodeType.YANG_AUGMENT_NODE;
import static org.onosproject.yang.compiler.datamodel.YangSchemaNodeType.YANG_MULTI_INSTANCE_NODE;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.BOOLEAN;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.EMPTY;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.INT16;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.INT32;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.INT64;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.INT8;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.LEAFREF;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.UINT16;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.UINT32;
import static org.onosproject.yang.compiler.datamodel.utils.builtindatatype.YangDataTypes.UINT8;
import static org.onosproject.yang.runtime.RuntimeHelper.PERIOD;
import static org.onosproject.yang.runtime.RuntimeHelper.UNDER_SCORE;
import static org.onosproject.yang.runtime.RuntimeHelper.getCapitalCase;
/**
* Representation of utility for YANG tree builder.
*/
final class ModelConverterUtil {
/**
* Static attribute for string value having null.
*/
static final String STR_NULL = "null";
static final String FALSE = "false";
static final String TRUE = "true";
private static final int ONE = 1;
private static final String ENUM_LEAF_IDENTIFIER = "$LeafIdentifier";
private static final Set<YangDataTypes> PRIMITIVE_TYPES =
new HashSet<>(Arrays.asList(INT8, INT16, INT32, INT64, UINT8,
UINT16, UINT32, BOOLEAN, EMPTY));
private static final String TO_STRING = "toString";
private static final String IS_VAL_SET = "isLeafValueSet";
private static final Base64.Encoder BASE64_BASIC_ENCODER = Base64.getEncoder();
// No instantiation.
private ModelConverterUtil() {
}
/**
* Returns the object of the node from the node info. Getting object for
* augment and case differs from other node.
*
* @param nodeInfo node info of the holder
* @param yangNode YANG node of the holder
* @return object of the parent
*/
static Object getParentObjectOfNode(DataTreeNodeInfo nodeInfo,
YangNode yangNode) {
Object object;
if (yangNode instanceof YangCase) {
object = nodeInfo.getCaseObject();
} else if (yangNode instanceof YangAugment) {
object = nodeInfo.getAugmentObject();
} else {
object = nodeInfo.getYangObject();
}
return object;
}
/**
* Returns the value of an attribute, in a class object. The attribute
* name is taken from the YANG node java name.
*
* @param nodeObj object of the node
* @param fieldName name of the attribute
* @return object of the attribute
* @throws NoSuchMethodException method not found exception
*/
static Object getAttributeOfObject(Object nodeObj, String fieldName)
throws NoSuchMethodException {
Class<?> nodeClass = nodeObj.getClass();
Method getterMethod;
try {
getterMethod = nodeClass.getDeclaredMethod(fieldName);
return getterMethod.invoke(nodeObj);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new ModelConverterException(e);
}
}
/**
* Returns the value of an attribute, in a class object. The attribute
* name is taken from the YANG node java name.
*
* @param nodeObj object of the node
* @param fieldName name of the attribute
* @return object of the attribute
* @throws NoSuchMethodException method not found exception
*/
static Object getAugmentObject(Object nodeObj, String fieldName)
throws NoSuchMethodException {
Class<?> nodeClass = nodeObj.getClass().getSuperclass();
Method getterMethod;
try {
getterMethod = nodeClass.getDeclaredMethod(fieldName);
return getterMethod.invoke(nodeObj);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new ModelConverterException(e);
}
}
/**
* Returns the object of the declared method in parent class by invoking
* through the child class object.
*
* @param childClass child class which inherits the parent class
* @param methodName name of the declared method
* @return value of the method
*/
static Object getAttributeFromInheritance(Object childClass,
String methodName) {
Class<?> parentClass = childClass.getClass().getSuperclass();
Method getterMethod;
try {
getterMethod = parentClass.getDeclaredMethod(methodName);
return getterMethod.invoke(childClass);
} catch (InvocationTargetException | NoSuchMethodException |
IllegalAccessException e) {
throw new ModelConverterException(e);
}
}
/**
* Returns interface class from an implementation class object.
*
* @param obj implementation class object
* @return interface class
*/
static Class<?> getInterfaceClassFromImplClass(Object obj) {
Class<?>[] interfaces = obj.getClass().getInterfaces();
if (interfaces.length > ONE) {
// TODO: Need to handle when impl class has more than one interface.
throw new ModelConverterException("Implementation class having more than one" +
" interface is not handled");
}
return interfaces[0];
}
/**
* Returns true, if data type of leaf is primitive data type; false
* otherwise.
*
* @param yangType leaf type
* @return true if data type is primitive; false otherwise
*/
static boolean isTypePrimitive(YangType yangType) {
if (yangType.getDataType() == LEAFREF) {
YangLeafRef leafRef =
(YangLeafRef) yangType.getDataTypeExtendedInfo();
return isPrimitiveDataType(leafRef.getEffectiveDataType()
.getDataType());
}
return isPrimitiveDataType(yangType.getDataType());
}
/**
* Returns the registered class from the YSR of the module node where
* augment is present.
*
* @param curNode current augment node
* @param registry schema registry
* @return class loader of module
*/
static Class<?> getClassLoaderForAugment(
YangNode curNode, DefaultYangModelRegistry registry) {
return registry.getRegisteredClass(curNode);
}
/**
* Returns true, if the leaf data is actually set; false otherwise.
*
* @param holder leaf holder
* @param nodeObj object if the node
* @param javaName java name of the leaf
* @return status of the value set flag
*/
static boolean isLeafValueSet(YangSchemaNode holder, Object nodeObj,
String javaName) {
Class<?> nodeClass = nodeObj.getClass();
// Appends the enum inner package to the interface class package.
String enumPackage = holder.getJavaPackage() + PERIOD +
getCapitalCase(holder.getJavaClassNameOrBuiltInType()) +
ENUM_LEAF_IDENTIFIER;
ClassLoader classLoader = nodeClass.getClassLoader();
Class leafEnum;
try {
leafEnum = classLoader.loadClass(enumPackage);
Method getterMethod = nodeClass.getMethod(IS_VAL_SET, leafEnum);
// Gets the value of the enum.
Enum<?> value = Enum.valueOf(leafEnum, javaName.toUpperCase());
// Invokes the method with the value of enum as param.
return (boolean) getterMethod.invoke(nodeObj, value);
} catch (IllegalAccessException | InvocationTargetException |
ClassNotFoundException | NoSuchMethodException e) {
throw new ModelConverterException(e);
}
}
/**
* Returns the string value from the respective data types of the
* leaf/leaf-list.
*
* @param holder leaf/leaf-list holder
* @param holderObj leaf/leaf-list holder object
* @param leaf leaf schema object?
* @param name leaf/leaf-list name
* @param fieldObj object of the leaf/leaf-list field
* @param dataType type of the leaf/leaf-list
* @return finalized object
*/
static Object getObjFromType(YangSchemaNode holder, Object holderObj,
Object leaf, String name, Object fieldObj,
YangType dataType) {
if (fieldObj == null) {
throw new ModelConverterException("Value of " + holder.getName()
+ " is null");
}
YangDataTypes type = dataType.getDataType();
switch (type) {
case INT8:
case INT16:
case INT32:
case INT64:
case UINT8:
case UINT16:
case UINT32:
case UINT64:
case STRING:
case BOOLEAN:
case DECIMAL64:
case EMPTY:
return fieldObj;
case INSTANCE_IDENTIFIER:
case ENUMERATION:
return String.valueOf(fieldObj).trim();
case BINARY:
return BASE64_BASIC_ENCODER.encodeToString((byte[]) fieldObj);
case BITS:
return getBitsValue(holder, holderObj, name, fieldObj).trim();
case IDENTITYREF:
YangIdentityRef ir = (YangIdentityRef) dataType
.getDataTypeExtendedInfo();
if (ir.isInGrouping()) {
return String.valueOf(fieldObj).trim();
}
return getIdentityRefValue(fieldObj, ir, holderObj);
case LEAFREF:
YangLeafRef leafRef = (YangLeafRef) dataType
.getDataTypeExtendedInfo();
Object refLeaf = leafRef.getReferredLeafOrLeafList();
if (refLeaf instanceof YangLeaf) {
holder = ((YangSchemaNode) ((YangLeaf) refLeaf)
.getContainedIn());
name = ((YangLeaf) refLeaf).getName();
} else if (refLeaf instanceof YangLeafList) {
holder = ((YangSchemaNode) ((YangLeafList) refLeaf)
.getContainedIn());
name = ((YangLeafList) refLeaf).getName();
}
return getObjFromType(holder, holderObj, leaf, name, fieldObj,
leafRef.getEffectiveDataType());
case DERIVED:
case UNION:
String val = String.valueOf(fieldObj).trim();
return ((LeafSchemaContext) leaf).fromString(val);
default:
throw new ModelConverterException(
"Unsupported data type. Cannot be processed.");
}
}
/**
* Returns the string values for the data type bits.
*
* @param holder leaf/leaf-list holder
* @param holderObj leaf/leaf-list holder object
* @param name leaf/leaf-list name
* @param fieldObj object of the leaf/leaf-list field
* @return string value for bits type
*/
private static String getBitsValue(YangSchemaNode holder, Object holderObj,
String name, Object fieldObj) {
Class<?> holderClass = holderObj.getClass();
String interfaceName = holder.getJavaClassNameOrBuiltInType();
String className = interfaceName.toLowerCase() + PERIOD +
getCapitalCase(name);
String pkgName = holder.getJavaPackage() + PERIOD + className;
ClassLoader classLoader = holderClass.getClassLoader();
Class<?> bitClass;
try {
bitClass = classLoader.loadClass(pkgName);
Method getterMethod = bitClass.getDeclaredMethod(
TO_STRING, fieldObj.getClass());
return String.valueOf(getterMethod.invoke(null, fieldObj));
} catch (ClassNotFoundException | NoSuchMethodException |
InvocationTargetException | IllegalAccessException e) {
throw new ModelConverterException(e);
}
}
/**
* Returns the string value of the type identity-ref.
*
* @param fieldObj object of the leaf/leaf-list field
* @param ir YANG identity ref
* @param holderObj leaf/leaf-list holder object
* @return string value for identity ref type
*/
private static String getIdentityRefValue(Object fieldObj, YangIdentityRef ir,
Object holderObj) {
YangIdentity id = getDerivedIdentity(fieldObj, ir);
if (id == null) {
throw new ModelConverterException("Value for identity is invalid");
}
String idName = id.getJavaClassNameOrBuiltInType();
String idPkg = id.getJavaPackage() + PERIOD + getCapitalCase(idName);
String methodName = idName + getCapitalCase(TO_STRING);
Class<?> holderClass = holderObj.getClass();
ClassLoader classLoader = holderClass.getClassLoader();
Class<?> idClass;
try {
idClass = classLoader.loadClass(idPkg);
Method method = idClass.getDeclaredMethod(methodName);
return String.valueOf(method.invoke(fieldObj)).trim();
} catch (ClassNotFoundException | NoSuchMethodException |
InvocationTargetException | IllegalAccessException e) {
throw new ModelConverterException(e);
}
}
private static YangIdentity getDerivedIdentity(Object fieldObj,
YangIdentityRef ir) {
YangIdentity id = ir.getReferredIdentity();
String idName = id.getJavaClassNameOrBuiltInType();
String[] objValue = fieldObj.toString().split("\\.");
String value = objValue[objValue.length - 1];
if (value.equalsIgnoreCase(idName)) {
return id;
}
List<YangIdentity> identities = id.getExtendList();
if (identities != null && !identities.isEmpty()) {
for (YangIdentity identity : identities) {
if (identity.getJavaClassNameOrBuiltInType()
.equalsIgnoreCase(value)) {
return identity;
}
}
}
return null;
}
/**
* Returns true, if the data type is primitive; false otherwise.
*
* @param dataType data type
* @return true if the data type is primitive; false otherwise
*/
private static boolean isPrimitiveDataType(YangDataTypes dataType) {
return PRIMITIVE_TYPES.contains(dataType);
}
/**
* Returns true, if processing of the node is not required; false otherwise.
* For the nodes such as notification, RPC, augment there is a different
* flow, so these nodes are skipped in normal conditions.
*
* @param yangNode node to be checked
* @return true if node processing is not required; false otherwise.
*/
static boolean isNonProcessableNode(YangNode yangNode) {
return yangNode != null &&
yangNode instanceof YangNotification ||
yangNode instanceof YangRpc ||
yangNode instanceof YangAugment;
}
/**
* Returns true, if multi instance node; false otherwise.
*
* @param yangNode YANG node
* @return true, if multi instance node; false otherwise.
*/
static boolean isMultiInstanceNode(YangNode yangNode) {
return yangNode.getYangSchemaNodeType() == YANG_MULTI_INSTANCE_NODE;
}
/**
* Returns true, if augment node; false otherwise.
*
* @param yangNode YANG node
* @return true, if augment node; false otherwise.
*/
static boolean isAugmentNode(YangNode yangNode) {
return yangNode.getYangSchemaNodeType() == YANG_AUGMENT_NODE;
}
/**
* Returns string for throwing error when empty object is given as input
* to YTB.
*
* @param objName name of the object
* @return error message
*/
static String emptyObjErrMsg(String objName) {
return "The " + objName + " given for tree creation cannot be null";
}
/**
* Returns the java name for the nodes, leaf/leaf-list.
*
* @param node YANG node
* @return node java name
*/
static String getJavaName(Object node) {
return ((YangSchemaNode) node).getJavaAttributeName();
}
/**
* Returns true, if the string is not null and non-empty; false otherwise.
*
* @param str string value
* @return true, if the string is not null and non-empty; false otherwise.
*/
static boolean nonEmpty(String str) {
return str != null && !str.isEmpty();
}
/**
* Returns true when the node processing of RPC and notification is
* completed; false otherwise. For RPC and notification, processing of
* other nodes are invalid, so once node gets completed, it must be stopped.
*
* @param curNode current node
* @param type current traversal of the node
* @return true, if the node processing is completed; false otherwise.
*/
static boolean isNodeProcessCompleted(YangNode curNode, TraversalType type) {
return type == PARENT &&
curNode instanceof YangNotification ||
curNode instanceof YangOutput;
}
/**
* Returns package for the given atomic path.
*
* @param path atomic path
* @return package for the given path
*/
static String fetchPackage(AtomicPath path) {
switch (path.type()) {
case SINGLE_INSTANCE_NODE:
SingleInstanceNode sin = (SingleInstanceNode) path;
return sin.container().getName();
case MULTI_INSTANCE_NODE:
MultiInstanceNode min = (MultiInstanceNode) path;
return min.listClass().getName();
case SINGLE_INSTANCE_LEAF_VALUE_NODE:
SingleInstanceLeaf sil = (SingleInstanceLeaf) path;
return getNameWithOutSpecialChar(
sil.leafIdentifier().toString().toLowerCase());
case MULTI_INSTANCE_LEAF_VALUE_NODE:
MultiInstanceLeaf mil = (MultiInstanceLeaf) path;
return getNameWithOutSpecialChar(
mil.leafIdentifier().toString().toLowerCase());
default:
throw new ModelConverterException("leaf/leaf-list can't be at this " +
"position");
}
}
/**
* Removes underscore from the given name.
*
* @param name name of instance
* @return name
*/
private static String getNameWithOutSpecialChar(String name) {
String[] str = name.split(UNDER_SCORE);
StringBuilder builder = new StringBuilder();
for (String s : str) {
builder.append(s);
}
return builder.toString();
}
/**
* Returns leaf object when value is present. For primitive types, in
* order to avoid default values, the value select is set or not is checked
* and then returned.
*
* @param holder leaf holder
* @param leaf YANG leaf
* @param parentObj leaf holder object
* @param leafObj object of leaf type
* @param isRoot if it is root leaf object
* @return processed leaf object
*/
static Object getLeafObject(YangSchemaNode holder, YangLeaf leaf,
Object parentObj, Object leafObj,
boolean isRoot) {
String jLeaf = getJavaName(leaf);
YangType<?> type = leaf.getDataType();
if (!isRoot && isTypePrimitive(type)) {
if (!isLeafValueSet(holder, parentObj, jLeaf)) {
return null;
}
}
if (leafObj == null) {
return null;
}
return getObjFromType(holder, parentObj, leaf, jLeaf,
leafObj, type);
}
/**
* Returns processed leaf-list objects from the data type.
*
* @param holder leaf-list holder
* @param leafList YANG leaf-list
* @param parentObj leaf-list holder object
* @param objects leaf-list objects
* @return processed leaf-list objects
*/
static Set<Object> getLeafListObject(YangSchemaNode holder,
YangLeafList leafList,
Object parentObj,
List<Object> objects) {
Set<Object> leafListVal = new LinkedHashSet<>();
YangType<?> type = leafList.getDataType();
for (Object object : objects) {
Object obj = getObjFromType(holder, parentObj, leafList,
getJavaName(leafList), object, type);
leafListVal.add(obj);
}
return leafListVal;
}
/**
* Returns the value as true if direct or referred type from leaf-ref or
* derived points to empty data type; false otherwise.
*
* @param dataType type of the leaf
* @return true if type is empty; false otherwise.
*/
static boolean isTypeEmpty(YangType<?> dataType) {
switch (dataType.getDataType()) {
case EMPTY:
return true;
case LEAFREF:
YangLeafRef leafRef = (YangLeafRef) dataType
.getDataTypeExtendedInfo();
return isTypeEmpty(leafRef.getEffectiveDataType());
case DERIVED:
YangDerivedInfo info = (YangDerivedInfo) dataType
.getDataTypeExtendedInfo();
YangDataTypes type = info.getEffectiveBuiltInType();
return type == EMPTY;
default:
return false;
}
}
}